aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2019-04-29 08:38:50 -0400
committerGitHub <noreply@github.com>2019-04-29 08:38:50 -0400
commit799490a02e9bea575bf34c39f045ef0883539f05 (patch)
tree278cada683564db36997cf3f96c92513b89c3096
parentd58ea974baff1b51a43d2e2bf4b287ff11991a8d (diff)
parent37e88e77cdf00e3555599dd4004d03548bd95dcf (diff)
downloadservo-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 -->
-rw-r--r--components/config/prefs.rs3
-rw-r--r--components/layout/query.rs2
-rw-r--r--components/layout_thread/dom_wrapper.rs156
-rw-r--r--components/layout_thread/lib.rs14
-rw-r--r--components/malloc_size_of/lib.rs6
-rw-r--r--components/script/devtools.rs4
-rw-r--r--components/script/dom/bindings/htmlconstructor.rs4
-rw-r--r--components/script/dom/bindings/trace.rs25
-rw-r--r--components/script/dom/create.rs6
-rw-r--r--components/script/dom/cssstyledeclaration.rs8
-rw-r--r--components/script/dom/cssstylerule.rs8
-rw-r--r--components/script/dom/cssstylesheet.rs12
-rw-r--r--components/script/dom/customelementregistry.rs21
-rw-r--r--components/script/dom/document.rs482
-rw-r--r--components/script/dom/documentfragment.rs26
-rw-r--r--components/script/dom/documentorshadowroot.rs312
-rw-r--r--components/script/dom/element.rs261
-rw-r--r--components/script/dom/htmlbaseelement.rs10
-rw-r--r--components/script/dom/htmlbodyelement.rs8
-rwxr-xr-xcomponents/script/dom/htmlbuttonelement.rs6
-rw-r--r--components/script/dom/htmlelement.rs10
-rw-r--r--components/script/dom/htmlfieldsetelement.rs4
-rwxr-xr-xcomponents/script/dom/htmlformelement.rs7
-rw-r--r--components/script/dom/htmlheadelement.rs8
-rw-r--r--components/script/dom/htmliframeelement.rs15
-rw-r--r--components/script/dom/htmlimageelement.rs13
-rwxr-xr-xcomponents/script/dom/htmlinputelement.rs6
-rw-r--r--components/script/dom/htmllabelelement.rs4
-rw-r--r--components/script/dom/htmllegendelement.rs6
-rw-r--r--components/script/dom/htmllinkelement.rs22
-rw-r--r--components/script/dom/htmlmapelement.rs4
-rw-r--r--components/script/dom/htmlmediaelement.rs4
-rw-r--r--components/script/dom/htmlmetaelement.rs19
-rw-r--r--components/script/dom/htmloptionelement.rs8
-rw-r--r--components/script/dom/htmlscriptelement.rs14
-rwxr-xr-xcomponents/script/dom/htmlselectelement.rs7
-rw-r--r--components/script/dom/htmlsourceelement.rs6
-rw-r--r--components/script/dom/htmlstyleelement.rs22
-rwxr-xr-xcomponents/script/dom/htmltextareaelement.rs8
-rw-r--r--components/script/dom/htmltitleelement.rs8
-rw-r--r--components/script/dom/macros.rs28
-rw-r--r--components/script/dom/mod.rs3
-rw-r--r--components/script/dom/mutationobserver.rs30
-rw-r--r--components/script/dom/node.rs346
-rw-r--r--components/script/dom/range.rs79
-rw-r--r--components/script/dom/raredata.rs42
-rw-r--r--components/script/dom/servoparser/html.rs2
-rw-r--r--components/script/dom/servoparser/mod.rs4
-rw-r--r--components/script/dom/shadowroot.rs268
-rw-r--r--components/script/dom/stylesheetlist.rs69
-rw-r--r--components/script/dom/virtualmethods.rs10
-rw-r--r--components/script/dom/webidls/Document.webidl14
-rw-r--r--components/script/dom/webidls/DocumentOrShadowRoot.webidl18
-rw-r--r--components/script/dom/webidls/Element.webidl2
-rw-r--r--components/script/dom/webidls/Node.webidl8
-rw-r--r--components/script/dom/webidls/ShadowRoot.webidl17
-rw-r--r--components/script/lib.rs6
-rw-r--r--components/script/script_thread.rs8
-rw-r--r--components/script/stylesheet_loader.rs12
-rw-r--r--components/script/stylesheet_set.rs70
-rw-r--r--components/script/webdriver_handlers.rs4
-rw-r--r--components/script_layout_interface/wrapper_traits.rs3
-rw-r--r--components/style/author_styles.rs1
-rw-r--r--components/style/stylesheet_set.rs10
-rw-r--r--resources/prefs.json1
-rw-r--r--tests/unit/script/size_of.rs14
-rw-r--r--tests/wpt/metadata/css/cssom/interfaces.html.ini3
-rw-r--r--tests/wpt/metadata/dom/interfaces.html.ini24
-rw-r--r--tests/wpt/mozilla/meta/MANIFEST.json37
-rw-r--r--tests/wpt/mozilla/meta/mozilla/partial_shadow_dom.html.ini2
-rw-r--r--tests/wpt/mozilla/meta/mozilla/partial_shadow_dom_layout_style.html.ini2
-rw-r--r--tests/wpt/mozilla/tests/mozilla/interfaces.html1
-rw-r--r--tests/wpt/mozilla/tests/mozilla/partial_shadow_dom.html41
-rw-r--r--tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style.html22
-rw-r--r--tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style_ref.html11
75 files changed, 2080 insertions, 701 deletions
diff --git a/components/config/prefs.rs b/components/config/prefs.rs
index e8b8b9a1e7e..76e70933bde 100644
--- a/components/config/prefs.rs
+++ b/components/config/prefs.rs
@@ -222,6 +222,9 @@ mod gen {
enabled: bool,
}
},
+ shadowdom: {
+ enabled: bool,
+ },
svg: {
enabled: bool,
},
diff --git a/components/layout/query.rs b/components/layout/query.rs
index 7a795e387fe..a74eda165e2 100644
--- a/components/layout/query.rs
+++ b/components/layout/query.rs
@@ -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;
}
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))
}
diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs
index 839fac79006..b44bbb54b96 100644
--- a/components/layout_thread/lib.rs
+++ b/components/layout_thread/lib.rs
@@ -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());
diff --git a/components/malloc_size_of/lib.rs b/components/malloc_size_of/lib.rs
index 629cb1f0824..bb4fe1a6adc 100644
--- a/components/malloc_size_of/lib.rs
+++ b/components/malloc_size_of/lib.rs
@@ -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 {
diff --git a/components/script/devtools.rs b/components/script/devtools.rs
index 7a24d959980..087930042e6 100644
--- a/components/script/devtools.rs
+++ b/components/script/devtools.rs
@@ -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)
})
}
diff --git a/components/script/dom/bindings/htmlconstructor.rs b/components/script/dom/bindings/htmlconstructor.rs
index 5654b52938b..a13b6fa9ecb 100644
--- a/components/script/dom/bindings/htmlconstructor.rs
+++ b/components/script/dom/bindings/htmlconstructor.rs
@@ -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;
diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs
index f1734d74b55..56805e4d9fe 100644
--- a/components/script/dom/bindings/trace.rs
+++ b/components/script/dom/bindings/trace.rs
@@ -118,6 +118,7 @@ use std::sync::atomic::{AtomicBool, AtomicUsize};
use std::sync::{Arc, Mutex};
use std::time::{Instant, SystemTime};
use style::attr::{AttrIdentifier, AttrValue, LengthOrPercentageOrAuto};
+use style::author_styles::AuthorStyles;
use style::context::QuirksMode;
use style::dom::OpaqueNode;
use style::element_state::*;
@@ -125,10 +126,11 @@ use style::media_queries::MediaList;
use style::properties::PropertyDeclarationBlock;
use style::selector_parser::{PseudoElement, Snapshot};
use style::shared_lock::{Locked as StyleLocked, SharedRwLock as StyleSharedRwLock};
-use style::stylesheet_set::DocumentStylesheetSet;
+use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet};
use style::stylesheets::keyframes_rule::Keyframe;
use style::stylesheets::{CssRules, FontFaceRule, KeyframesRule, MediaRule, Stylesheet};
use style::stylesheets::{ImportRule, NamespaceRule, StyleRule, SupportsRule, ViewportRule};
+use style::stylist::CascadeData;
use style::values::specified::Length;
use tendril::fmt::UTF8;
use tendril::stream::LossyDecoder;
@@ -497,6 +499,7 @@ unsafe_no_jsmanaged_fields!(HTMLMediaElementFetchContext);
unsafe_no_jsmanaged_fields!(Rotation3D<f64>, Transform2D<f32>, Transform3D<f64>);
unsafe_no_jsmanaged_fields!(Point2D<f32>, Vector2D<f32>, Rect<Au>);
unsafe_no_jsmanaged_fields!(Rect<f32>, RigidTransform3D<f64>);
+unsafe_no_jsmanaged_fields!(CascadeData);
unsafe impl<'a> JSTraceable for &'a str {
#[inline]
@@ -715,6 +718,26 @@ where
}
}
+unsafe impl<S> JSTraceable for AuthorStylesheetSet<S>
+where
+ S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
+{
+ unsafe fn trace(&self, tracer: *mut JSTracer) {
+ for s in self.iter() {
+ s.trace(tracer)
+ }
+ }
+}
+
+unsafe impl<S> JSTraceable for AuthorStyles<S>
+where
+ S: JSTraceable + ::style::stylesheets::StylesheetInDocument + PartialEq + 'static,
+{
+ unsafe fn trace(&self, tracer: *mut JSTracer) {
+ self.stylesheets.trace(tracer)
+ }
+}
+
unsafe impl<Sink> JSTraceable for LossyDecoder<Sink>
where
Sink: JSTraceable + TendrilSink<UTF8>,
diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs
index 461e37a28de..6ffb38f753a 100644
--- a/components/script/dom/create.rs
+++ b/components/script/dom/create.rs
@@ -5,9 +5,11 @@
use crate::dom::bindings::error::{report_pending_exception, throw_dom_exception};
use crate::dom::bindings::reflector::DomObject;
use crate::dom::bindings::root::DomRoot;
-use crate::dom::customelementregistry::{is_valid_custom_element_name, upgrade_element};
+use crate::dom::customelementregistry::{
+ is_valid_custom_element_name, upgrade_element, CustomElementState,
+};
use crate::dom::document::Document;
-use crate::dom::element::{CustomElementCreationMode, CustomElementState, Element, ElementCreator};
+use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlanchorelement::HTMLAnchorElement;
use crate::dom::htmlareaelement::HTMLAreaElement;
diff --git a/components/script/dom/cssstyledeclaration.rs b/components/script/dom/cssstyledeclaration.rs
index 2dabe431496..d623f46005a 100644
--- a/components/script/dom/cssstyledeclaration.rs
+++ b/components/script/dom/cssstyledeclaration.rs
@@ -13,7 +13,7 @@ use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssrule::CSSRule;
use crate::dom::element::Element;
-use crate::dom::node::{document_from_node, window_from_node, Node};
+use crate::dom::node::{document_from_node, stylesheets_owner_from_node, window_from_node, Node};
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_arc::Arc;
@@ -115,9 +115,7 @@ impl CSSStyleOwner {
if changed {
// If this is changed, see also
// CSSStyleRule::SetSelectorText, which does the same thing.
- rule.global()
- .as_window()
- .Document()
+ stylesheets_owner_from_node(rule.parent_stylesheet().owner().upcast::<Node>())
.invalidate_stylesheets();
}
result
@@ -246,7 +244,7 @@ impl CSSStyleDeclaration {
},
CSSStyleOwner::Element(ref el) => {
let node = el.upcast::<Node>();
- if !node.is_in_doc() {
+ if !node.is_connected() {
// TODO: Node should be matched against the style rules of this window.
// Firefox is currently the only browser to implement this.
return DOMString::new();
diff --git a/components/script/dom/cssstylerule.rs b/components/script/dom/cssstylerule.rs
index c76e3bdd938..14d7cdcdbef 100644
--- a/components/script/dom/cssstylerule.rs
+++ b/components/script/dom/cssstylerule.rs
@@ -3,7 +3,6 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
use crate::dom::bindings::codegen::Bindings::CSSStyleRuleBinding::{self, CSSStyleRuleMethods};
-use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
@@ -11,6 +10,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::cssrule::{CSSRule, SpecificCSSRule};
use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner};
use crate::dom::cssstylesheet::CSSStyleSheet;
+use crate::dom::node::{stylesheets_owner_from_node, Node};
use crate::dom::window::Window;
use cssparser::ToCss;
use cssparser::{Parser as CssParser, ParserInput as CssParserInput};
@@ -118,11 +118,7 @@ impl CSSStyleRuleMethods for CSSStyleRule {
let mut guard = self.cssrule.shared_lock().write();
let stylerule = self.stylerule.write_with(&mut guard);
mem::swap(&mut stylerule.selectors, &mut s);
- // It seems like we will want to avoid having to invalidate all
- // stylesheets eventually!
- self.global()
- .as_window()
- .Document()
+ stylesheets_owner_from_node(self.cssrule.parent_stylesheet().owner().upcast::<Node>())
.invalidate_stylesheets();
}
}
diff --git a/components/script/dom/cssstylesheet.rs b/components/script/dom/cssstylesheet.rs
index 2def47d7aaa..66d2fd8a339 100644
--- a/components/script/dom/cssstylesheet.rs
+++ b/components/script/dom/cssstylesheet.rs
@@ -4,13 +4,14 @@
use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding;
use crate::dom::bindings::codegen::Bindings::CSSStyleSheetBinding::CSSStyleSheetMethods;
-use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
+use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::reflector::{reflect_dom_object, DomObject};
use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
use crate::dom::cssrulelist::{CSSRuleList, RulesSource};
use crate::dom::element::Element;
+use crate::dom::node::{stylesheets_owner_from_node, Node};
use crate::dom::stylesheet::StyleSheet;
use crate::dom::window::Window;
use dom_struct::dom_struct;
@@ -64,6 +65,10 @@ impl CSSStyleSheet {
)
}
+ pub fn owner(&self) -> DomRoot<Element> {
+ DomRoot::from_ref(&*self.owner)
+ }
+
fn rulelist(&self) -> DomRoot<CSSRuleList> {
self.rulelist.or_init(|| {
let rules = self.style_stylesheet.contents.rules.clone();
@@ -81,10 +86,7 @@ impl CSSStyleSheet {
pub fn set_disabled(&self, disabled: bool) {
if self.style_stylesheet.set_disabled(disabled) {
- self.global()
- .as_window()
- .Document()
- .invalidate_stylesheets();
+ stylesheets_owner_from_node(self.owner().upcast::<Node>()).invalidate_stylesheets();
}
}
diff --git a/components/script/dom/customelementregistry.rs b/components/script/dom/customelementregistry.rs
index abefc4eca58..e1b3163bf4a 100644
--- a/components/script/dom/customelementregistry.rs
+++ b/components/script/dom/customelementregistry.rs
@@ -23,10 +23,10 @@ use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::domexception::{DOMErrorName, DOMException};
-use crate::dom::element::{CustomElementState, Element};
+use crate::dom::element::Element;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::{document_from_node, window_from_node, Node};
+use crate::dom::node::{document_from_node, window_from_node, Node, ShadowIncluding};
use crate::dom::promise::Promise;
use crate::dom::window::Window;
use crate::microtask::Microtask;
@@ -47,6 +47,21 @@ use std::ops::Deref;
use std::ptr;
use std::rc::Rc;
+/// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
+#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
+pub enum CustomElementState {
+ Undefined,
+ Failed,
+ Uncustomized,
+ Custom,
+}
+
+impl Default for CustomElementState {
+ fn default() -> CustomElementState {
+ CustomElementState::Uncustomized
+ }
+}
+
/// <https://html.spec.whatwg.org/multipage/#customelementregistry>
#[dom_struct]
pub struct CustomElementRegistry {
@@ -364,7 +379,7 @@ impl CustomElementRegistryMethods for CustomElementRegistry {
// Steps 14-15
for candidate in document
.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<Element>)
{
let is = candidate.get_is();
diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs
index ed011c8029d..1a6714f5927 100644
--- a/components/script/dom/document.rs
+++ b/components/script/dom/document.rs
@@ -43,6 +43,7 @@ use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::customelementregistry::CustomElementDefinition;
use crate::dom::customevent::CustomEvent;
use crate::dom::documentfragment::DocumentFragment;
+use crate::dom::documentorshadowroot::{DocumentOrShadowRoot, StyleSheetInDocument};
use crate::dom::documenttype::DocumentType;
use crate::dom::domimplementation::DOMImplementation;
use crate::dom::element::CustomElementCreationMode;
@@ -67,16 +68,14 @@ use crate::dom::htmlheadelement::HTMLHeadElement;
use crate::dom::htmlhtmlelement::HTMLHtmlElement;
use crate::dom::htmliframeelement::HTMLIFrameElement;
use crate::dom::htmlimageelement::HTMLImageElement;
-use crate::dom::htmlmetaelement::HTMLMetaElement;
use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult};
use crate::dom::htmltitleelement::HTMLTitleElement;
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::location::Location;
use crate::dom::messageevent::MessageEvent;
use crate::dom::mouseevent::MouseEvent;
-use crate::dom::node::VecPreOrderInsertionHelper;
use crate::dom::node::{self, document_from_node, window_from_node, CloneChildrenFlag};
-use crate::dom::node::{LayoutNodeHelpers, Node, NodeDamage, NodeFlags};
+use crate::dom::node::{LayoutNodeHelpers, Node, NodeDamage, NodeFlags, ShadowIncluding};
use crate::dom::nodeiterator::NodeIterator;
use crate::dom::nodelist::NodeList;
use crate::dom::pagetransitionevent::PageTransitionEvent;
@@ -86,8 +85,9 @@ use crate::dom::progressevent::ProgressEvent;
use crate::dom::promise::Promise;
use crate::dom::range::Range;
use crate::dom::servoparser::ServoParser;
+use crate::dom::shadowroot::ShadowRoot;
use crate::dom::storageevent::StorageEvent;
-use crate::dom::stylesheetlist::StyleSheetList;
+use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
use crate::dom::text::Text;
use crate::dom::touch::Touch;
use crate::dom::touchevent::TouchEvent;
@@ -101,6 +101,7 @@ use crate::dom::windowproxy::WindowProxy;
use crate::fetch::FetchCanceller;
use crate::script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
use crate::script_thread::{MainThreadScriptMsg, ScriptThread};
+use crate::stylesheet_set::StylesheetSetRef;
use crate::task::TaskBox;
use crate::task_source::{TaskSource, TaskSourceName};
use crate::timers::OneshotTimerCallback;
@@ -113,7 +114,6 @@ use euclid::Point2D;
use html5ever::{LocalName, Namespace, QualName};
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
-use js::jsapi::JS_GetRuntime;
use js::jsapi::{JSContext, JSObject, JSRuntime};
use keyboard_types::{Key, KeyState, Modifiers};
use metrics::{
@@ -132,7 +132,7 @@ use num_traits::ToPrimitive;
use profile_traits::ipc as profile_ipc;
use profile_traits::time::{TimerMetadata, TimerMetadataFrameType, TimerMetadataReflowType};
use ref_slice::ref_slice;
-use script_layout_interface::message::{Msg, NodesFromPointQueryType, QueryMsg, ReflowGoal};
+use script_layout_interface::message::{Msg, ReflowGoal};
use script_traits::{AnimationState, DocumentActivity, MouseButton, MouseEventType};
use script_traits::{MsDuration, ScriptMsg, TouchEventType, TouchId, UntrustedNodeAddress};
use servo_arc::Arc;
@@ -144,7 +144,6 @@ use std::cell::{Cell, Ref, RefMut};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::{HashMap, HashSet, VecDeque};
use std::default::Default;
-use std::fmt;
use std::mem;
use std::ptr::NonNull;
use std::rc::Rc;
@@ -152,12 +151,12 @@ use std::time::{Duration, Instant};
use style::attr::AttrValue;
use style::context::QuirksMode;
use style::invalidation::element::restyle_hints::RestyleHint;
-use style::media_queries::{Device, MediaList, MediaType};
+use style::media_queries::{Device, MediaType};
use style::selector_parser::{RestyleDamage, Snapshot};
-use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
+use style::shared_lock::SharedRwLock as StyleSharedRwLock;
use style::str::{split_html_space_chars, str_join};
use style::stylesheet_set::DocumentStylesheetSet;
-use style::stylesheets::{CssRule, Origin, OriginSet, Stylesheet};
+use style::stylesheets::{Origin, OriginSet, Stylesheet};
use url::percent_encoding::percent_decode;
use url::Host;
@@ -220,52 +219,11 @@ impl PendingRestyle {
}
}
-#[derive(Clone, JSTraceable, MallocSizeOf)]
-#[must_root]
-struct StyleSheetInDocument {
- #[ignore_malloc_size_of = "Arc"]
- sheet: Arc<Stylesheet>,
- owner: Dom<Element>,
-}
-
-impl fmt::Debug for StyleSheetInDocument {
- fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- self.sheet.fmt(formatter)
- }
-}
-
-impl PartialEq for StyleSheetInDocument {
- fn eq(&self, other: &Self) -> bool {
- Arc::ptr_eq(&self.sheet, &other.sheet)
- }
-}
-
-impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
- fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
- self.sheet.origin(guard)
- }
-
- fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
- self.sheet.quirks_mode(guard)
- }
-
- fn enabled(&self) -> bool {
- self.sheet.enabled()
- }
-
- fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
- self.sheet.media(guard)
- }
-
- fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
- self.sheet.rules(guard)
- }
-}
-
/// <https://dom.spec.whatwg.org/#document>
#[dom_struct]
pub struct Document {
node: Node,
+ document_or_shadow_root: DocumentOrShadowRoot,
window: Dom<Window>,
implementation: MutNullableDom<DOMImplementation>,
#[ignore_malloc_size_of = "type from external crate"]
@@ -419,6 +377,10 @@ pub struct Document {
delayed_tasks: DomRefCell<Vec<Box<dyn TaskBox>>>,
/// https://html.spec.whatwg.org/multipage/#completely-loaded
completely_loaded: Cell<bool>,
+ /// Set of shadow roots connected to the document tree.
+ shadow_roots: DomRefCell<HashSet<Dom<ShadowRoot>>>,
+ /// Whether any of the shadow roots need the stylesheets flushed.
+ shadow_roots_styles_changed: Cell<bool>,
}
#[derive(JSTraceable, MallocSizeOf)]
@@ -634,7 +596,7 @@ impl Document {
pub fn refresh_base_element(&self) {
let base = self
.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLBaseElement>)
.find(|element| {
element
@@ -684,7 +646,7 @@ impl Document {
}
pub fn content_and_heritage_changed(&self, node: &Node) {
- if node.is_in_doc() {
+ if node.is_connected() {
node.note_dirty_descendants();
}
@@ -720,55 +682,23 @@ impl Document {
/// Remove any existing association between the provided id and any elements in this document.
pub fn unregister_named_element(&self, to_unregister: &Element, id: Atom) {
- debug!(
- "Removing named element from document {:p}: {:p} id={}",
- self, to_unregister, id
- );
- // Limit the scope of the borrow because id_map might be borrowed again by
- // GetElementById through the following sequence of calls
- // reset_form_owner_for_listeners -> reset_form_owner -> GetElementById
- {
- let mut id_map = self.id_map.borrow_mut();
- let is_empty = match id_map.get_mut(&id) {
- None => false,
- Some(elements) => {
- let position = elements
- .iter()
- .position(|element| &**element == to_unregister)
- .expect("This element should be in registered.");
- elements.remove(position);
- elements.is_empty()
- },
- };
- if is_empty {
- id_map.remove(&id);
- }
- }
+ self.document_or_shadow_root
+ .unregister_named_element(&self.id_map, to_unregister, &id);
self.reset_form_owner_for_listeners(&id);
}
/// Associate an element present in this document with the provided id.
pub fn register_named_element(&self, element: &Element, id: Atom) {
- debug!(
- "Adding named element to document {:p}: {:p} id={}",
- self, element, id
- );
- assert!(element.upcast::<Node>().is_in_doc());
- assert!(!id.is_empty());
-
let root = self.GetDocumentElement().expect(
"The element is in the document, so there must be a document \
element.",
);
-
- // Limit the scope of the borrow because id_map might be borrowed again by
- // GetElementById through the following sequence of calls
- // reset_form_owner_for_listeners -> reset_form_owner -> GetElementById
- {
- let mut id_map = self.id_map.borrow_mut();
- let elements = id_map.entry(id.clone()).or_insert(Vec::new());
- elements.insert_pre_order(element, root.upcast::<Node>());
- }
+ self.document_or_shadow_root.register_named_element(
+ &self.id_map,
+ element,
+ &id,
+ DomRoot::from_ref(root.upcast::<Node>()),
+ );
self.reset_form_owner_for_listeners(&id);
}
@@ -872,7 +802,7 @@ impl Document {
};
let doc_node = self.upcast::<Node>();
doc_node
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast)
.find(|node| check_anchor(&node))
.map(DomRoot::upcast)
@@ -981,7 +911,7 @@ impl Document {
pub fn dirty_all_nodes(&self) {
let root = self.upcast::<Node>();
- for node in root.traverse_preorder() {
+ for node in root.traverse_preorder(ShadowIncluding::Yes) {
node.dirty(NodeDamage::OtherNodeDamage)
}
}
@@ -1005,7 +935,7 @@ impl Document {
let el = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
- node.inclusive_ancestors()
+ node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
@@ -1189,7 +1119,7 @@ impl Document {
let maybe_new_target = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
- node.inclusive_ancestors()
+ node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
@@ -1225,7 +1155,7 @@ impl Document {
if !old_target_is_ancestor_of_new_target {
for element in old_target
.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
{
element.set_hover_state(false);
@@ -1243,7 +1173,7 @@ impl Document {
if let Some(ref new_target) = maybe_new_target {
for element in new_target
.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
{
if element.hover_state() {
@@ -1285,7 +1215,7 @@ impl Document {
let el = node_address.and_then(|address| {
let node = unsafe { node::from_untrusted_node_address(js_runtime, address) };
- node.inclusive_ancestors()
+ node.inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.next()
});
@@ -2276,7 +2206,7 @@ impl Document {
/// Iterate over all iframes in the document.
pub fn iter_iframes(&self) -> impl Iterator<Item = DomRoot<HTMLIFrameElement>> {
self.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<HTMLIFrameElement>)
}
@@ -2395,21 +2325,6 @@ impl Document {
!self.has_browsing_context || !url_has_network_scheme(&self.url())
}
- pub fn nodes_from_point(
- &self,
- client_point: &Point2D<f32>,
- reflow_goal: NodesFromPointQueryType,
- ) -> Vec<UntrustedNodeAddress> {
- if !self
- .window
- .layout_reflow(QueryMsg::NodesFromPointQuery(*client_point, reflow_goal))
- {
- return vec![];
- };
-
- self.window.layout().nodes_from_point_response()
- }
-
/// <https://html.spec.whatwg.org/multipage/#look-up-a-custom-element-definition>
pub fn lookup_custom_element_definition(
&self,
@@ -2485,6 +2400,9 @@ pub trait LayoutDocumentHelpers {
unsafe fn will_paint(&self);
unsafe fn quirks_mode(&self) -> QuirksMode;
unsafe fn style_shared_lock(&self) -> &StyleSharedRwLock;
+ unsafe fn shadow_roots(&self) -> Vec<LayoutDom<ShadowRoot>>;
+ unsafe fn shadow_roots_styles_changed(&self) -> bool;
+ unsafe fn flush_shadow_roots_stylesheets(&self);
}
#[allow(unsafe_code)]
@@ -2500,12 +2418,12 @@ impl LayoutDocumentHelpers for LayoutDom<Document> {
let mut elements = (*self.unsafe_get())
.pending_restyles
.borrow_mut_for_layout();
- // Elements were in a document when they were adding to this list, but that
+ // Elements were in a document when they were added to this list, but that
// may no longer be true when the next layout occurs.
let result = elements
.drain()
.map(|(k, v)| (k.to_layout(), v))
- .filter(|&(ref k, _)| k.upcast::<Node>().get_flag(NodeFlags::IS_IN_DOC))
+ .filter(|&(ref k, _)| k.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED))
.collect();
result
}
@@ -2529,6 +2447,26 @@ impl LayoutDocumentHelpers for LayoutDom<Document> {
unsafe fn style_shared_lock(&self) -> &StyleSharedRwLock {
(*self.unsafe_get()).style_shared_lock()
}
+
+ #[inline]
+ unsafe fn shadow_roots(&self) -> Vec<LayoutDom<ShadowRoot>> {
+ (*self.unsafe_get())
+ .shadow_roots
+ .borrow_for_layout()
+ .iter()
+ .map(|sr| sr.to_layout())
+ .collect()
+ }
+
+ #[inline]
+ unsafe fn shadow_roots_styles_changed(&self) -> bool {
+ (*self.unsafe_get()).shadow_roots_styles_changed()
+ }
+
+ #[inline]
+ unsafe fn flush_shadow_roots_stylesheets(&self) {
+ (*self.unsafe_get()).flush_shadow_roots_stylesheets()
+ }
}
// https://html.spec.whatwg.org/multipage/#is-a-registrable-domain-suffix-of-or-is-equal-to
@@ -2637,21 +2575,23 @@ impl Document {
.and_then(|charset| Encoding::for_label(charset.as_str().as_bytes()))
.unwrap_or(UTF_8);
+ let has_browsing_context = has_browsing_context == HasBrowsingContext::Yes;
Document {
node: Node::new_document_node(),
+ document_or_shadow_root: DocumentOrShadowRoot::new(window),
window: Dom::from_ref(window),
- has_browsing_context: has_browsing_context == HasBrowsingContext::Yes,
+ has_browsing_context,
implementation: Default::default(),
content_type,
last_modified: last_modified,
url: DomRefCell::new(url),
// https://dom.spec.whatwg.org/#concept-document-quirks
quirks_mode: Cell::new(QuirksMode::NoQuirks),
+ id_map: DomRefCell::new(HashMap::new()),
// https://dom.spec.whatwg.org/#concept-document-encoding
encoding: Cell::new(encoding),
is_html_document: is_html_document == IsHTMLDocument::HTMLDocument,
activity: Cell::new(activity),
- id_map: DomRefCell::new(HashMap::new()),
tag_map: DomRefCell::new(HashMap::new()),
tagns_map: DomRefCell::new(HashMap::new()),
classes_map: DomRefCell::new(HashMap::new()),
@@ -2689,7 +2629,7 @@ impl Document {
deferred_scripts: Default::default(),
asap_in_order_scripts_list: Default::default(),
asap_scripts_set: Default::default(),
- scripting_enabled: has_browsing_context == HasBrowsingContext::Yes,
+ scripting_enabled: has_browsing_context,
animation_frame_ident: Cell::new(0),
animation_frame_list: DomRefCell::new(vec![]),
running_animation_callbacks: Cell::new(false),
@@ -2735,6 +2675,8 @@ impl Document {
completely_loaded: Cell::new(false),
script_and_layout_blockers: Cell::new(0),
delayed_tasks: Default::default(),
+ shadow_roots: DomRefCell::new(HashSet::new()),
+ shadow_roots_styles_changed: Cell::new(false),
}
}
@@ -2853,7 +2795,7 @@ impl Document {
let maybe_node = doc.deref().map(Castable::upcast::<Node>);
let iter = maybe_node
.iter()
- .flat_map(|node| node.traverse_preorder())
+ .flat_map(|node| node.traverse_preorder(ShadowIncluding::No))
.filter(|node| callback(&node));
NodeList::new_simple_list(&self.window, iter)
}
@@ -2893,93 +2835,10 @@ impl Document {
Device::new(MediaType::screen(), viewport_size, device_pixel_ratio)
}
- /// Remove a stylesheet owned by `owner` from the list of document sheets.
- #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
- pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
- self.window()
- .layout_chan()
- .send(Msg::RemoveStylesheet(s.clone()))
- .unwrap();
-
- let guard = s.shared_lock.read();
-
- // FIXME(emilio): Would be nice to remove the clone, etc.
- self.stylesheets.borrow_mut().remove_stylesheet(
- None,
- StyleSheetInDocument {
- sheet: s.clone(),
- owner: Dom::from_ref(owner),
- },
- &guard,
- );
- }
-
- /// Add a stylesheet owned by `owner` to the list of document sheets, in the
- /// correct tree position.
- #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
- pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
- // FIXME(emilio): It'd be nice to unify more code between the elements
- // that own stylesheets, but StylesheetOwner is more about loading
- // them...
- debug_assert!(
- owner.as_stylesheet_owner().is_some() || owner.is::<HTMLMetaElement>(),
- "Wat"
- );
-
- let mut stylesheets = self.stylesheets.borrow_mut();
- let insertion_point = stylesheets
- .iter()
- .map(|(sheet, _origin)| sheet)
- .find(|sheet_in_doc| {
- owner
- .upcast::<Node>()
- .is_before(sheet_in_doc.owner.upcast())
- })
- .cloned();
-
- self.window()
- .layout_chan()
- .send(Msg::AddStylesheet(
- sheet.clone(),
- insertion_point.as_ref().map(|s| s.sheet.clone()),
- ))
- .unwrap();
-
- let sheet = StyleSheetInDocument {
- sheet,
- owner: Dom::from_ref(owner),
- };
-
- let lock = self.style_shared_lock();
- let guard = lock.read();
-
- match insertion_point {
- Some(ip) => {
- stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
- },
- None => {
- stylesheets.append_stylesheet(None, sheet, &guard);
- },
- }
- }
-
- /// Returns the number of document stylesheets.
- pub fn stylesheet_count(&self) -> usize {
- self.stylesheets.borrow().len()
- }
-
pub fn salvageable(&self) -> bool {
self.salvageable.get()
}
- pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
- let stylesheets = self.stylesheets.borrow();
-
- stylesheets
- .get(Origin::Author, index)
- .and_then(|s| s.owner.upcast::<Node>().get_cssom_stylesheet())
- }
-
/// <https://html.spec.whatwg.org/multipage/#appropriate-template-contents-owner-document>
pub fn appropriate_template_contents_owner_document(&self) -> DomRoot<Document> {
self.appropriate_template_contents_owner_document
@@ -3270,6 +3129,92 @@ impl Document {
}
}
}
+
+ pub fn register_shadow_root(&self, shadow_root: &ShadowRoot) {
+ self.shadow_roots
+ .borrow_mut()
+ .insert(Dom::from_ref(shadow_root));
+ self.invalidate_shadow_roots_stylesheets();
+ }
+
+ pub fn unregister_shadow_root(&self, shadow_root: &ShadowRoot) {
+ let mut shadow_roots = self.shadow_roots.borrow_mut();
+ shadow_roots.remove(&Dom::from_ref(shadow_root));
+ }
+
+ pub fn invalidate_shadow_roots_stylesheets(&self) {
+ self.shadow_roots_styles_changed.set(true);
+ }
+
+ pub fn shadow_roots_styles_changed(&self) -> bool {
+ self.shadow_roots_styles_changed.get()
+ }
+
+ pub fn flush_shadow_roots_stylesheets(&self) {
+ if !self.shadow_roots_styles_changed.get() {
+ return;
+ }
+ self.shadow_roots_styles_changed.set(false);
+ }
+
+ pub fn stylesheet_count(&self) -> usize {
+ self.stylesheets.borrow().len()
+ }
+
+ pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
+ let stylesheets = self.stylesheets.borrow();
+
+ stylesheets
+ .get(Origin::Author, index)
+ .and_then(|s| s.owner.upcast::<Node>().get_cssom_stylesheet())
+ }
+
+ /// Add a stylesheet owned by `owner` to the list of document sheets, in the
+ /// correct tree position.
+ #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
+ pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
+ let stylesheets = &mut *self.stylesheets.borrow_mut();
+ let insertion_point = stylesheets
+ .iter()
+ .map(|(sheet, _origin)| sheet)
+ .find(|sheet_in_doc| {
+ owner
+ .upcast::<Node>()
+ .is_before(sheet_in_doc.owner.upcast())
+ })
+ .cloned();
+
+ self.window
+ .layout_chan()
+ .send(Msg::AddStylesheet(
+ sheet.clone(),
+ insertion_point.as_ref().map(|s| s.sheet.clone()),
+ ))
+ .unwrap();
+
+ DocumentOrShadowRoot::add_stylesheet(
+ owner,
+ StylesheetSetRef::Document(stylesheets),
+ sheet,
+ insertion_point,
+ self.style_shared_lock(),
+ );
+ }
+
+ /// Remove a stylesheet owned by `owner` from the list of document sheets.
+ #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
+ pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
+ self.window
+ .layout_chan()
+ .send(Msg::RemoveStylesheet(s.clone()))
+ .unwrap();
+
+ DocumentOrShadowRoot::remove_stylesheet(
+ owner,
+ s,
+ StylesheetSetRef::Document(&mut *self.stylesheets.borrow_mut()),
+ )
+ }
}
impl Element {
@@ -3301,8 +3246,12 @@ impl ProfilerMetadataFactory for Document {
impl DocumentMethods for Document {
// https://drafts.csswg.org/cssom/#dom-document-stylesheets
fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
- self.stylesheet_list
- .or_init(|| StyleSheetList::new(&self.window, Dom::from_ref(&self)))
+ self.stylesheet_list.or_init(|| {
+ StyleSheetList::new(
+ &self.window,
+ StyleSheetListOwner::Document(Dom::from_ref(self)),
+ )
+ })
}
// https://dom.spec.whatwg.org/#dom-document-implementation
@@ -3317,16 +3266,11 @@ impl DocumentMethods for Document {
// https://html.spec.whatwg.org/multipage/#dom-document-activeelement
fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
- // TODO: Step 2.
-
- match self.get_focused_element() {
- Some(element) => Some(element), // Step 3. and 4.
- None => match self.GetBody() {
- // Step 5.
- Some(body) => Some(DomRoot::upcast(body)),
- None => self.GetDocumentElement(),
- },
- }
+ self.document_or_shadow_root.get_active_element(
+ self.get_focused_element(),
+ self.GetBody(),
+ self.GetDocumentElement(),
+ )
}
// https://html.spec.whatwg.org/multipage/#dom-document-hasfocus
@@ -3643,7 +3587,7 @@ impl DocumentMethods for Document {
// https://dom.spec.whatwg.org/#dom-document-importnode
fn ImportNode(&self, node: &Node, deep: bool) -> Fallible<DomRoot<Node>> {
// Step 1.
- if node.is::<Document>() {
+ if node.is::<Document>() || node.is::<ShadowRoot>() {
return Err(Error::NotSupported);
}
@@ -3665,9 +3609,14 @@ impl DocumentMethods for Document {
}
// Step 2.
- Node::adopt(node, self);
+ if node.is::<ShadowRoot>() {
+ return Err(Error::HierarchyRequest);
+ }
// Step 3.
+ Node::adopt(node, self);
+
+ // Step 4.
Ok(DomRoot::from_ref(node))
}
@@ -3805,7 +3754,7 @@ impl DocumentMethods for Document {
} else {
// Step 2.
root.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.find(|node| node.is::<HTMLTitleElement>())
}
});
@@ -3852,7 +3801,7 @@ impl DocumentMethods for Document {
} else if root.namespace() == &ns!(html) {
let elem = root
.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.find(|node| node.is::<HTMLTitleElement>());
match elem {
Some(elem) => elem,
@@ -4219,7 +4168,7 @@ impl DocumentMethods for Document {
{
// Step 1.
let mut elements = root
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter(|node| filter_by_name(&name, &node))
.peekable();
if let Some(first) = elements.next() {
@@ -4273,82 +4222,24 @@ impl DocumentMethods for Document {
SetOnreadystatechange
);
- #[allow(unsafe_code)]
// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
- let x = *x as f32;
- let y = *y as f32;
- let point = &Point2D::new(x, y);
- let window = window_from_node(self);
- let viewport = window.window_size().initial_viewport;
-
- if self.browsing_context().is_none() {
- return None;
- }
-
- if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
- return None;
- }
-
- match self
- .nodes_from_point(point, NodesFromPointQueryType::Topmost)
- .first()
- {
- Some(address) => {
- let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
- let node = unsafe { node::from_untrusted_node_address(js_runtime, *address) };
- let parent_node = node.GetParentNode().unwrap();
- let element_ref = node
- .downcast::<Element>()
- .unwrap_or_else(|| parent_node.downcast::<Element>().unwrap());
-
- Some(DomRoot::from_ref(element_ref))
- },
- None => self.GetDocumentElement(),
- }
+ self.document_or_shadow_root.element_from_point(
+ x,
+ y,
+ self.GetDocumentElement(),
+ self.has_browsing_context,
+ )
}
- #[allow(unsafe_code)]
// https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
- let x = *x as f32;
- let y = *y as f32;
- let point = &Point2D::new(x, y);
- let window = window_from_node(self);
- let viewport = window.window_size().initial_viewport;
-
- if self.browsing_context().is_none() {
- return vec![];
- }
-
- // Step 2
- if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
- return vec![];
- }
-
- let js_runtime = unsafe { JS_GetRuntime(window.get_cx()) };
-
- // Step 1 and Step 3
- let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
- let mut elements: Vec<DomRoot<Element>> = nodes
- .iter()
- .flat_map(|&untrusted_node_address| {
- let node = unsafe {
- node::from_untrusted_node_address(js_runtime, untrusted_node_address)
- };
- DomRoot::downcast::<Element>(node)
- })
- .collect();
-
- // Step 4
- if let Some(root_element) = self.GetDocumentElement() {
- if elements.last() != Some(&root_element) {
- elements.push(root_element);
- }
- }
-
- // Step 5
- elements
+ self.document_or_shadow_root.elements_from_point(
+ x,
+ y,
+ self.GetDocumentElement(),
+ self.has_browsing_context,
+ )
}
// https://html.spec.whatwg.org/multipage/#dom-document-open
@@ -4403,7 +4294,10 @@ impl DocumentMethods for Document {
}
// Step 8
- for node in self.upcast::<Node>().traverse_preorder() {
+ for node in self
+ .upcast::<Node>()
+ .traverse_preorder(ShadowIncluding::Yes)
+ {
node.upcast::<EventTarget>().remove_all_listeners();
}
diff --git a/components/script/dom/documentfragment.rs b/components/script/dom/documentfragment.rs
index 2e504750947..1561b0d007e 100644
--- a/components/script/dom/documentfragment.rs
+++ b/components/script/dom/documentfragment.rs
@@ -2,13 +2,14 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::DocumentFragmentBinding;
use crate::dom::bindings::codegen::Bindings::DocumentFragmentBinding::DocumentFragmentMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
use crate::dom::bindings::error::{ErrorResult, Fallible};
use crate::dom::bindings::inheritance::Castable;
-use crate::dom::bindings::root::DomRoot;
+use crate::dom::bindings::root::{Dom, DomRoot};
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::element::Element;
@@ -18,18 +19,22 @@ use crate::dom::nodelist::NodeList;
use crate::dom::window::Window;
use dom_struct::dom_struct;
use servo_atoms::Atom;
+use std::collections::HashMap;
// https://dom.spec.whatwg.org/#documentfragment
#[dom_struct]
pub struct DocumentFragment {
node: Node,
+ /// Caches for the getElement methods
+ id_map: DomRefCell<HashMap<Atom, Vec<Dom<Element>>>>,
}
impl DocumentFragment {
/// Creates a new DocumentFragment.
- fn new_inherited(document: &Document) -> DocumentFragment {
+ pub fn new_inherited(document: &Document) -> DocumentFragment {
DocumentFragment {
node: Node::new_inherited(document),
+ id_map: DomRefCell::new(HashMap::new()),
}
}
@@ -46,6 +51,10 @@ impl DocumentFragment {
Ok(DocumentFragment::new(&document))
}
+
+ pub fn id_map(&self) -> &DomRefCell<HashMap<Atom, Vec<Dom<Element>>>> {
+ &self.id_map
+ }
}
impl DocumentFragmentMethods for DocumentFragment {
@@ -57,16 +66,11 @@ impl DocumentFragmentMethods for DocumentFragment {
// https://dom.spec.whatwg.org/#dom-nonelementparentnode-getelementbyid
fn GetElementById(&self, id: DOMString) -> Option<DomRoot<Element>> {
- let node = self.upcast::<Node>();
let id = Atom::from(id);
- node.traverse_preorder()
- .filter_map(DomRoot::downcast::<Element>)
- .find(
- |descendant| match descendant.get_attribute(&ns!(), &local_name!("id")) {
- None => false,
- Some(attr) => *attr.value().as_atom() == id,
- },
- )
+ self.id_map
+ .borrow()
+ .get(&id)
+ .map(|ref elements| DomRoot::from_ref(&*(*elements)[0]))
}
// https://dom.spec.whatwg.org/#dom-parentnode-firstelementchild
diff --git a/components/script/dom/documentorshadowroot.rs b/components/script/dom/documentorshadowroot.rs
new file mode 100644
index 00000000000..8a29d16d013
--- /dev/null
+++ b/components/script/dom/documentorshadowroot.rs
@@ -0,0 +1,312 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeBinding::NodeMethods;
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::num::Finite;
+use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::element::Element;
+use crate::dom::htmlelement::HTMLElement;
+use crate::dom::htmlmetaelement::HTMLMetaElement;
+use crate::dom::node::{self, Node, VecPreOrderInsertionHelper};
+use crate::dom::window::Window;
+use crate::stylesheet_set::StylesheetSetRef;
+use euclid::Point2D;
+use js::jsapi::JS_GetRuntime;
+use script_layout_interface::message::{NodesFromPointQueryType, QueryMsg};
+use script_traits::UntrustedNodeAddress;
+use servo_arc::Arc;
+use servo_atoms::Atom;
+use std::collections::HashMap;
+use std::fmt;
+use style::context::QuirksMode;
+use style::invalidation::media_queries::{MediaListKey, ToMediaListKey};
+use style::media_queries::MediaList;
+use style::shared_lock::{SharedRwLock as StyleSharedRwLock, SharedRwLockReadGuard};
+use style::stylesheets::{CssRule, Origin, Stylesheet};
+
+#[derive(Clone, JSTraceable, MallocSizeOf)]
+#[must_root]
+pub struct StyleSheetInDocument {
+ #[ignore_malloc_size_of = "Arc"]
+ pub sheet: Arc<Stylesheet>,
+ pub owner: Dom<Element>,
+}
+
+impl fmt::Debug for StyleSheetInDocument {
+ fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ self.sheet.fmt(formatter)
+ }
+}
+
+impl PartialEq for StyleSheetInDocument {
+ fn eq(&self, other: &Self) -> bool {
+ Arc::ptr_eq(&self.sheet, &other.sheet)
+ }
+}
+
+impl ToMediaListKey for StyleSheetInDocument {
+ fn to_media_list_key(&self) -> MediaListKey {
+ self.sheet.to_media_list_key()
+ }
+}
+
+impl ::style::stylesheets::StylesheetInDocument for StyleSheetInDocument {
+ fn origin(&self, guard: &SharedRwLockReadGuard) -> Origin {
+ self.sheet.origin(guard)
+ }
+
+ fn quirks_mode(&self, guard: &SharedRwLockReadGuard) -> QuirksMode {
+ self.sheet.quirks_mode(guard)
+ }
+
+ fn enabled(&self) -> bool {
+ self.sheet.enabled()
+ }
+
+ fn media<'a>(&'a self, guard: &'a SharedRwLockReadGuard) -> Option<&'a MediaList> {
+ self.sheet.media(guard)
+ }
+
+ fn rules<'a, 'b: 'a>(&'a self, guard: &'b SharedRwLockReadGuard) -> &'a [CssRule] {
+ self.sheet.rules(guard)
+ }
+}
+
+// https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
+#[must_root]
+#[derive(JSTraceable, MallocSizeOf)]
+pub struct DocumentOrShadowRoot {
+ window: Dom<Window>,
+}
+
+impl DocumentOrShadowRoot {
+ pub fn new(window: &Window) -> Self {
+ Self {
+ window: Dom::from_ref(window),
+ }
+ }
+
+ pub fn nodes_from_point(
+ &self,
+ client_point: &Point2D<f32>,
+ reflow_goal: NodesFromPointQueryType,
+ ) -> Vec<UntrustedNodeAddress> {
+ if !self
+ .window
+ .layout_reflow(QueryMsg::NodesFromPointQuery(*client_point, reflow_goal))
+ {
+ return vec![];
+ };
+
+ self.window.layout().nodes_from_point_response()
+ }
+
+ #[allow(unsafe_code)]
+ // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
+ pub fn element_from_point(
+ &self,
+ x: Finite<f64>,
+ y: Finite<f64>,
+ document_element: Option<DomRoot<Element>>,
+ has_browsing_context: bool,
+ ) -> Option<DomRoot<Element>> {
+ let x = *x as f32;
+ let y = *y as f32;
+ let point = &Point2D::new(x, y);
+ let viewport = self.window.window_size().initial_viewport;
+
+ if !has_browsing_context {
+ return None;
+ }
+
+ if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
+ return None;
+ }
+
+ match self
+ .nodes_from_point(point, NodesFromPointQueryType::Topmost)
+ .first()
+ {
+ Some(address) => {
+ let js_runtime = unsafe { JS_GetRuntime(self.window.get_cx()) };
+ let node = unsafe { node::from_untrusted_node_address(js_runtime, *address) };
+ let parent_node = node.GetParentNode().unwrap();
+ let element_ref = node
+ .downcast::<Element>()
+ .unwrap_or_else(|| parent_node.downcast::<Element>().unwrap());
+
+ Some(DomRoot::from_ref(element_ref))
+ },
+ None => document_element,
+ }
+ }
+
+ #[allow(unsafe_code)]
+ // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
+ pub fn elements_from_point(
+ &self,
+ x: Finite<f64>,
+ y: Finite<f64>,
+ document_element: Option<DomRoot<Element>>,
+ has_browsing_context: bool,
+ ) -> Vec<DomRoot<Element>> {
+ let x = *x as f32;
+ let y = *y as f32;
+ let point = &Point2D::new(x, y);
+ let viewport = self.window.window_size().initial_viewport;
+
+ if !has_browsing_context {
+ return vec![];
+ }
+
+ // Step 2
+ if x < 0.0 || y < 0.0 || x > viewport.width || y > viewport.height {
+ return vec![];
+ }
+
+ let js_runtime = unsafe { JS_GetRuntime(self.window.get_cx()) };
+
+ // Step 1 and Step 3
+ let nodes = self.nodes_from_point(point, NodesFromPointQueryType::All);
+ let mut elements: Vec<DomRoot<Element>> = nodes
+ .iter()
+ .flat_map(|&untrusted_node_address| {
+ let node = unsafe {
+ node::from_untrusted_node_address(js_runtime, untrusted_node_address)
+ };
+ DomRoot::downcast::<Element>(node)
+ })
+ .collect();
+
+ // Step 4
+ if let Some(root_element) = document_element {
+ if elements.last() != Some(&root_element) {
+ elements.push(root_element);
+ }
+ }
+
+ // Step 5
+ elements
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-document-activeelement
+ pub fn get_active_element(
+ &self,
+ focused_element: Option<DomRoot<Element>>,
+ body: Option<DomRoot<HTMLElement>>,
+ document_element: Option<DomRoot<Element>>,
+ ) -> Option<DomRoot<Element>> {
+ // TODO: Step 2.
+
+ match focused_element {
+ Some(element) => Some(element), // Step 3. and 4.
+ None => match body {
+ // Step 5.
+ Some(body) => Some(DomRoot::upcast(body)),
+ None => document_element,
+ },
+ }
+ }
+
+ /// Remove a stylesheet owned by `owner` from the list of document sheets.
+ #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
+ pub fn remove_stylesheet(
+ owner: &Element,
+ s: &Arc<Stylesheet>,
+ mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
+ ) {
+ let guard = s.shared_lock.read();
+
+ // FIXME(emilio): Would be nice to remove the clone, etc.
+ stylesheets.remove_stylesheet(
+ None,
+ StyleSheetInDocument {
+ sheet: s.clone(),
+ owner: Dom::from_ref(owner),
+ },
+ &guard,
+ );
+ }
+
+ /// Add a stylesheet owned by `owner` to the list of document sheets, in the
+ /// correct tree position.
+ #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
+ pub fn add_stylesheet(
+ owner: &Element,
+ mut stylesheets: StylesheetSetRef<StyleSheetInDocument>,
+ sheet: Arc<Stylesheet>,
+ insertion_point: Option<StyleSheetInDocument>,
+ style_shared_lock: &StyleSharedRwLock,
+ ) {
+ // FIXME(emilio): It'd be nice to unify more code between the elements
+ // that own stylesheets, but StylesheetOwner is more about loading
+ // them...
+ debug_assert!(
+ owner.as_stylesheet_owner().is_some() || owner.is::<HTMLMetaElement>(),
+ "Wat"
+ );
+
+ let sheet = StyleSheetInDocument {
+ sheet,
+ owner: Dom::from_ref(owner),
+ };
+
+ let guard = style_shared_lock.read();
+
+ match insertion_point {
+ Some(ip) => {
+ stylesheets.insert_stylesheet_before(None, sheet, ip, &guard);
+ },
+ None => {
+ stylesheets.append_stylesheet(None, sheet, &guard);
+ },
+ }
+ }
+
+ /// Remove any existing association between the provided id and any elements in this document.
+ pub fn unregister_named_element(
+ &self,
+ id_map: &DomRefCell<HashMap<Atom, Vec<Dom<Element>>>>,
+ to_unregister: &Element,
+ id: &Atom,
+ ) {
+ debug!(
+ "Removing named element {:p}: {:p} id={}",
+ self, to_unregister, id
+ );
+ let mut id_map = id_map.borrow_mut();
+ let is_empty = match id_map.get_mut(&id) {
+ None => false,
+ Some(elements) => {
+ let position = elements
+ .iter()
+ .position(|element| &**element == to_unregister)
+ .expect("This element should be in registered.");
+ elements.remove(position);
+ elements.is_empty()
+ },
+ };
+ if is_empty {
+ id_map.remove(&id);
+ }
+ }
+
+ /// Associate an element present in this document with the provided id.
+ pub fn register_named_element(
+ &self,
+ id_map: &DomRefCell<HashMap<Atom, Vec<Dom<Element>>>>,
+ element: &Element,
+ id: &Atom,
+ root: DomRoot<Node>,
+ ) {
+ debug!("Adding named element {:p}: {:p} id={}", self, element, id);
+ assert!(element.upcast::<Node>().is_connected());
+ assert!(!id.is_empty());
+ let mut id_map = id_map.borrow_mut();
+ let elements = id_map.entry(id.clone()).or_insert(Vec::new());
+ elements.insert_pre_order(element, &root);
+ }
+}
diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs
index c3d5fbb55d0..2d12e93774d 100644
--- a/components/script/dom/element.rs
+++ b/components/script/dom/element.rs
@@ -15,6 +15,7 @@ use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
use crate::dom::bindings::codegen::Bindings::FunctionBinding::Function;
use crate::dom::bindings::codegen::Bindings::HTMLTemplateElementBinding::HTMLTemplateElementMethods;
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
+use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootBinding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
@@ -32,7 +33,7 @@ use crate::dom::bindings::xmlname::{
use crate::dom::characterdata::CharacterData;
use crate::dom::create::create_element;
use crate::dom::customelementregistry::{
- CallbackReaction, CustomElementDefinition, CustomElementReaction,
+ CallbackReaction, CustomElementDefinition, CustomElementReaction, CustomElementState,
};
use crate::dom::document::{Document, LayoutDocumentHelpers};
use crate::dom::documentfragment::DocumentFragment;
@@ -71,11 +72,13 @@ use crate::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaEle
use crate::dom::mutationobserver::{Mutation, MutationObserver};
use crate::dom::namednodemap::NamedNodeMap;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{ChildrenMutation, LayoutNodeHelpers, Node};
-use crate::dom::node::{NodeDamage, NodeFlags, UnbindContext};
+use crate::dom::node::{BindContext, NodeDamage, NodeFlags, UnbindContext};
+use crate::dom::node::{ChildrenMutation, LayoutNodeHelpers, Node, ShadowIncluding};
use crate::dom::nodelist::NodeList;
use crate::dom::promise::Promise;
+use crate::dom::raredata::ElementRareData;
use crate::dom::servoparser::ServoParser;
+use crate::dom::shadowroot::ShadowRoot;
use crate::dom::text::Text;
use crate::dom::validation::Validatable;
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
@@ -103,7 +106,7 @@ use selectors::Element as SelectorsElement;
use servo_arc::Arc;
use servo_atoms::Atom;
use std::borrow::Cow;
-use std::cell::{Cell, Ref};
+use std::cell::{Cell, Ref, RefMut};
use std::default::Default;
use std::fmt;
use std::mem;
@@ -163,13 +166,7 @@ pub struct Element {
/// when it has exclusive access to the element.
#[ignore_malloc_size_of = "bitflags defined in rust-selectors"]
selector_flags: Cell<ElementSelectorFlags>,
- /// <https://html.spec.whatwg.org/multipage/#custom-element-reaction-queue>
- custom_element_reaction_queue: DomRefCell<Vec<CustomElementReaction>>,
- /// <https://dom.spec.whatwg.org/#concept-element-custom-element-definition>
- #[ignore_malloc_size_of = "Rc"]
- custom_element_definition: DomRefCell<Option<Rc<CustomElementDefinition>>>,
- /// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
- custom_element_state: Cell<CustomElementState>,
+ rare_data: DomRefCell<Option<Box<ElementRareData>>>,
}
impl fmt::Debug for Element {
@@ -199,15 +196,6 @@ pub enum CustomElementCreationMode {
Asynchronous,
}
-/// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
-#[derive(Clone, Copy, Eq, JSTraceable, MallocSizeOf, PartialEq)]
-pub enum CustomElementState {
- Undefined,
- Failed,
- Uncustomized,
- Custom,
-}
-
impl ElementCreator {
pub fn is_parser_created(&self) -> bool {
match *self {
@@ -244,6 +232,13 @@ impl FromStr for AdjacentPosition {
}
}
+/// Whether a shadow root hosts an User Agent widget.
+#[derive(PartialEq)]
+pub enum IsUserAgentWidget {
+ No,
+ Yes,
+}
+
//
// Element methods
//
@@ -294,9 +289,7 @@ impl Element {
class_list: Default::default(),
state: Cell::new(state),
selector_flags: Cell::new(ElementSelectorFlags::empty()),
- custom_element_reaction_queue: Default::default(),
- custom_element_definition: Default::default(),
- custom_element_state: Cell::new(CustomElementState::Uncustomized),
+ rare_data: Default::default(),
}
}
@@ -315,6 +308,8 @@ impl Element {
)
}
+ impl_rare_data!(ElementRareData);
+
pub fn restyle(&self, damage: NodeDamage) {
let doc = self.node.owner_doc();
let mut restyle = doc.ensure_pending_restyle(self);
@@ -337,49 +332,60 @@ impl Element {
}
pub fn set_custom_element_state(&self, state: CustomElementState) {
- self.custom_element_state.set(state);
+ self.ensure_rare_data().custom_element_state = state;
}
pub fn get_custom_element_state(&self) -> CustomElementState {
- self.custom_element_state.get()
+ if let Some(rare_data) = self.rare_data().as_ref() {
+ return rare_data.custom_element_state;
+ }
+ CustomElementState::Undefined
}
pub fn set_custom_element_definition(&self, definition: Rc<CustomElementDefinition>) {
- *self.custom_element_definition.borrow_mut() = Some(definition);
+ self.ensure_rare_data().custom_element_definition = Some(definition);
}
pub fn get_custom_element_definition(&self) -> Option<Rc<CustomElementDefinition>> {
- (*self.custom_element_definition.borrow()).clone()
+ self.rare_data().as_ref()?.custom_element_definition.clone()
}
pub fn push_callback_reaction(&self, function: Rc<Function>, args: Box<[Heap<JSVal>]>) {
- self.custom_element_reaction_queue
- .borrow_mut()
+ self.ensure_rare_data()
+ .custom_element_reaction_queue
.push(CustomElementReaction::Callback(function, args));
}
pub fn push_upgrade_reaction(&self, definition: Rc<CustomElementDefinition>) {
- self.custom_element_reaction_queue
- .borrow_mut()
+ self.ensure_rare_data()
+ .custom_element_reaction_queue
.push(CustomElementReaction::Upgrade(definition));
}
pub fn clear_reaction_queue(&self) {
- self.custom_element_reaction_queue.borrow_mut().clear();
+ if let Some(ref mut rare_data) = *self.rare_data_mut() {
+ rare_data.custom_element_reaction_queue.clear();
+ }
}
pub fn invoke_reactions(&self) {
- // TODO: This is not spec compliant, as this will allow some reactions to be processed
- // after clear_reaction_queue has been called.
- rooted_vec!(let mut reactions);
- while !self.custom_element_reaction_queue.borrow().is_empty() {
- mem::swap(
- &mut *reactions,
- &mut *self.custom_element_reaction_queue.borrow_mut(),
- );
+ loop {
+ rooted_vec!(let mut reactions);
+ match *self.rare_data_mut() {
+ Some(ref mut data) => {
+ mem::swap(&mut *reactions, &mut data.custom_element_reaction_queue)
+ },
+ None => break,
+ };
+
+ if reactions.is_empty() {
+ break;
+ }
+
for reaction in reactions.iter() {
reaction.invoke(self);
}
+
reactions.clear();
}
}
@@ -436,6 +442,71 @@ impl Element {
box_.clone_overflow_y() == overflow_y::computed_value::T::Hidden
})
}
+
+ fn shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
+ self.rare_data()
+ .as_ref()?
+ .shadow_root
+ .as_ref()
+ .map(|sr| DomRoot::from_ref(&**sr))
+ }
+
+ pub fn is_shadow_host(&self) -> bool {
+ self.shadow_root().is_some()
+ }
+
+ /// https://dom.spec.whatwg.org/#dom-element-attachshadow
+ /// XXX This is not exposed to web content yet. It is meant to be used
+ /// for UA widgets only.
+ pub fn attach_shadow(&self, is_ua_widget: IsUserAgentWidget) -> Fallible<DomRoot<ShadowRoot>> {
+ // Step 1.
+ if self.namespace != ns!(html) {
+ return Err(Error::NotSupported);
+ }
+
+ // Step 2.
+ match self.local_name() {
+ &local_name!("article") |
+ &local_name!("aside") |
+ &local_name!("blockquote") |
+ &local_name!("body") |
+ &local_name!("div") |
+ &local_name!("footer") |
+ &local_name!("h1") |
+ &local_name!("h2") |
+ &local_name!("h3") |
+ &local_name!("h4") |
+ &local_name!("h5") |
+ &local_name!("h6") |
+ &local_name!("header") |
+ &local_name!("main") |
+ &local_name!("nav") |
+ &local_name!("p") |
+ &local_name!("section") |
+ &local_name!("span") => {},
+ &local_name!("video") | &local_name!("audio")
+ if is_ua_widget == IsUserAgentWidget::Yes => {},
+ _ => return Err(Error::NotSupported),
+ };
+
+ // Step 3.
+ if self.is_shadow_host() {
+ return Err(Error::InvalidState);
+ }
+
+ // Steps 4, 5 and 6.
+ let shadow_root = ShadowRoot::new(self, &*self.node.owner_doc());
+ self.ensure_rare_data().shadow_root = Some(Dom::from_ref(&*shadow_root));
+ shadow_root
+ .upcast::<Node>()
+ .set_containing_shadow_root(&shadow_root);
+
+ if self.is_connected() {
+ self.node.owner_doc().register_shadow_root(&*shadow_root);
+ }
+
+ Ok(shadow_root)
+ }
}
#[allow(unsafe_code)]
@@ -534,6 +605,9 @@ pub trait LayoutElementHelpers {
fn get_state_for_layout(&self) -> ElementState;
fn insert_selector_flags(&self, flags: ElementSelectorFlags);
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool;
+ /// The shadow root this element is a host of.
+ #[allow(unsafe_code)]
+ unsafe fn get_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>>;
}
impl LayoutElementHelpers for LayoutDom<Element> {
@@ -932,7 +1006,7 @@ impl LayoutElementHelpers for LayoutDom<Element> {
unsafe {
let mut current_node = Some(self.upcast::<Node>());
while let Some(node) = current_node {
- current_node = node.parent_node_ref();
+ current_node = node.composed_parent_node_ref();
match node.downcast::<Element>().map(|el| el.unsafe_get()) {
Some(elem) => {
if let Some(attr) =
@@ -996,6 +1070,17 @@ impl LayoutElementHelpers for LayoutDom<Element> {
fn has_selector_flags(&self, flags: ElementSelectorFlags) -> bool {
unsafe { (*self.unsafe_get()).selector_flags.get().contains(flags) }
}
+
+ #[inline]
+ #[allow(unsafe_code)]
+ unsafe fn get_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>> {
+ (*self.unsafe_get())
+ .rare_data_for_layout()
+ .as_ref()?
+ .shadow_root
+ .as_ref()
+ .map(|sr| sr.to_layout())
+ }
}
impl Element {
@@ -1040,7 +1125,7 @@ impl Element {
let inclusive_ancestor_elements = self
.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Self>);
// Steps 3-4.
@@ -1156,7 +1241,7 @@ impl Element {
.unwrap()
} else {
self.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast)
.last()
.expect("We know inclusive_ancestors will return `self` which is an element")
@@ -1165,7 +1250,10 @@ impl Element {
// https://dom.spec.whatwg.org/#locate-a-namespace-prefix
pub fn lookup_prefix(&self, namespace: Namespace) -> Option<DOMString> {
- for node in self.upcast::<Node>().inclusive_ancestors() {
+ for node in self
+ .upcast::<Node>()
+ .inclusive_ancestors(ShadowIncluding::No)
+ {
let element = node.downcast::<Element>()?;
// Step 1.
if *element.namespace() == namespace {
@@ -2372,7 +2460,7 @@ impl ElementMethods for Element {
NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed),
// Step 4.
- NodeTypeId::DocumentFragment => {
+ NodeTypeId::DocumentFragment(_) => {
let body_elem = Element::create(
QualName::new(None, ns!(html), local_name!("body")),
None,
@@ -2586,6 +2674,13 @@ impl ElementMethods for Element {
let doc = document_from_node(self);
doc.enter_fullscreen(self)
}
+
+ // XXX Hidden under dom.shadowdom.enabled pref. Only exposed to be able
+ // to test partial Shadow DOM support for UA widgets.
+ // https://dom.spec.whatwg.org/#dom-element-attachshadow
+ fn AttachShadow(&self) -> Fallible<DomRoot<ShadowRoot>> {
+ self.attach_shadow(IsUserAgentWidget::No)
+ }
}
impl VirtualMethods for Element {
@@ -2658,21 +2753,34 @@ impl VirtualMethods for Element {
None
}
});
- if node.is_in_doc() {
+ let containing_shadow_root = self.upcast::<Node>().containing_shadow_root();
+ if node.is_connected() {
let value = attr.value().as_atom().clone();
match mutation {
AttributeMutation::Set(old_value) => {
if let Some(old_value) = old_value {
let old_value = old_value.as_atom().clone();
- doc.unregister_named_element(self, old_value);
+ if let Some(ref shadow_root) = containing_shadow_root {
+ shadow_root.unregister_named_element(self, old_value);
+ } else {
+ doc.unregister_named_element(self, old_value);
+ }
}
if value != atom!("") {
- doc.register_named_element(self, value);
+ if let Some(ref shadow_root) = containing_shadow_root {
+ shadow_root.register_named_element(self, value);
+ } else {
+ doc.register_named_element(self, value);
+ }
}
},
AttributeMutation::Removed => {
if value != atom!("") {
- doc.unregister_named_element(self, value);
+ if let Some(ref shadow_root) = containing_shadow_root {
+ shadow_root.unregister_named_element(self, value);
+ } else {
+ doc.unregister_named_element(self, value);
+ }
}
},
}
@@ -2704,22 +2812,37 @@ impl VirtualMethods for Element {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
if let Some(f) = self.as_maybe_form_control() {
f.bind_form_control_to_tree();
}
- if !tree_in_doc {
+ let doc = document_from_node(self);
+
+ if let Some(ref shadow_root) = self.shadow_root() {
+ doc.register_shadow_root(&shadow_root);
+ let shadow_root = shadow_root.upcast::<Node>();
+ shadow_root.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
+ for node in shadow_root.children() {
+ node.set_flag(NodeFlags::IS_CONNECTED, context.tree_connected);
+ node.bind_to_tree(context);
+ }
+ }
+
+ if !context.tree_connected {
return;
}
- let doc = document_from_node(self);
if let Some(ref value) = *self.id_attribute.borrow() {
- doc.register_named_element(self, value.clone());
+ if let Some(shadow_root) = self.upcast::<Node>().containing_shadow_root() {
+ shadow_root.register_named_element(self, value.clone());
+ } else {
+ doc.register_named_element(self, value.clone());
+ }
}
// This is used for layout optimization.
doc.increment_dom_count();
@@ -2732,11 +2855,22 @@ impl VirtualMethods for Element {
f.unbind_form_control_from_tree();
}
- if !context.tree_in_doc {
+ if !context.tree_connected {
return;
}
let doc = document_from_node(self);
+
+ if let Some(ref shadow_root) = self.shadow_root() {
+ doc.unregister_shadow_root(&shadow_root);
+ let shadow_root = shadow_root.upcast::<Node>();
+ shadow_root.set_flag(NodeFlags::IS_CONNECTED, false);
+ for node in shadow_root.children() {
+ node.set_flag(NodeFlags::IS_CONNECTED, false);
+ node.unbind_from_tree(context);
+ }
+ }
+
let fullscreen = doc.GetFullscreenElement();
if fullscreen.deref() == Some(self) {
doc.exit_fullscreen();
@@ -2797,11 +2931,18 @@ impl<'a> SelectorsElement for DomRoot<Element> {
}
fn parent_node_is_shadow_root(&self) -> bool {
- false
+ match self.upcast::<Node>().GetParentNode() {
+ None => false,
+ Some(node) => node.is::<ShadowRoot>(),
+ }
}
fn containing_shadow_host(&self) -> Option<Self> {
- None
+ if let Some(shadow_root) = self.upcast::<Node>().containing_shadow_root() {
+ Some(shadow_root.Host())
+ } else {
+ None
+ }
}
fn match_pseudo_element(
@@ -3111,7 +3252,7 @@ impl Element {
// https://html.spec.whatwg.org/multipage/#language
pub fn get_lang(&self) -> String {
self.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(|node| {
node.downcast::<Element>().and_then(|el| {
el.get_attribute(&ns!(xml), &local_name!("lang"))
@@ -3229,9 +3370,7 @@ impl Element {
/// <https://dom.spec.whatwg.org/#connected>
pub fn is_connected(&self) -> bool {
- let node = self.upcast::<Node>();
- let root = node.GetRootNode();
- root.is::<Document>()
+ self.upcast::<Node>().is_connected()
}
// https://html.spec.whatwg.org/multipage/#cannot-navigate
diff --git a/components/script/dom/htmlbaseelement.rs b/components/script/dom/htmlbaseelement.rs
index 6823925cb37..71aa235df62 100644
--- a/components/script/dom/htmlbaseelement.rs
+++ b/components/script/dom/htmlbaseelement.rs
@@ -11,7 +11,7 @@ use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::{document_from_node, Node, UnbindContext};
+use crate::dom::node::{document_from_node, BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -64,7 +64,7 @@ impl HTMLBaseElement {
/// Update the cached base element in response to binding or unbinding from
/// a tree.
pub fn bind_unbind(&self, tree_in_doc: bool) {
- if !tree_in_doc {
+ if !tree_in_doc || self.upcast::<Node>().containing_shadow_root().is_some() {
return;
}
@@ -119,9 +119,9 @@ impl VirtualMethods for HTMLBaseElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
- self.super_type().unwrap().bind_to_tree(tree_in_doc);
- self.bind_unbind(tree_in_doc);
+ fn bind_to_tree(&self, context: &BindContext) {
+ self.super_type().unwrap().bind_to_tree(context);
+ self.bind_unbind(context.tree_in_doc);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
diff --git a/components/script/dom/htmlbodyelement.rs b/components/script/dom/htmlbodyelement.rs
index 951643c7ea7..f59d163d163 100644
--- a/components/script/dom/htmlbodyelement.rs
+++ b/components/script/dom/htmlbodyelement.rs
@@ -14,7 +14,7 @@ use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use crate::dom::eventtarget::EventTarget;
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::{document_from_node, window_from_node, Node};
+use crate::dom::node::{document_from_node, window_from_node, BindContext, Node};
use crate::dom::virtualmethods::VirtualMethods;
use cssparser::RGBA;
use dom_struct::dom_struct;
@@ -149,12 +149,12 @@ impl VirtualMethods for HTMLBodyElement {
.attribute_affects_presentational_hints(attr)
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
- if !tree_in_doc {
+ if !context.tree_in_doc {
return;
}
diff --git a/components/script/dom/htmlbuttonelement.rs b/components/script/dom/htmlbuttonelement.rs
index 96b51b472c4..a7b8dacd060 100755
--- a/components/script/dom/htmlbuttonelement.rs
+++ b/components/script/dom/htmlbuttonelement.rs
@@ -18,7 +18,7 @@ use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmlformelement::{FormControl, FormDatum, FormDatumValue};
use crate::dom::htmlformelement::{FormSubmitter, ResetFrom, SubmittedFrom};
-use crate::dom::node::{document_from_node, window_from_node, Node, UnbindContext};
+use crate::dom::node::{document_from_node, window_from_node, BindContext, Node, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::validation::Validatable;
use crate::dom::validitystate::{ValidationFlags, ValidityState};
@@ -232,9 +232,9 @@ impl VirtualMethods for HTMLButtonElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.upcast::<Element>()
diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs
index ade940c536e..95ac9918996 100644
--- a/components/script/dom/htmlelement.rs
+++ b/components/script/dom/htmlelement.rs
@@ -28,7 +28,7 @@ use crate::dom::htmlhtmlelement::HTMLHtmlElement;
use crate::dom::htmlinputelement::{HTMLInputElement, InputType};
use crate::dom::htmllabelelement::HTMLLabelElement;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{Node, NodeFlags};
+use crate::dom::node::{BindContext, Node, NodeFlags, ShadowIncluding};
use crate::dom::nodelist::NodeList;
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
@@ -459,7 +459,7 @@ impl HTMLElementMethods for HTMLElement {
let element = self.upcast::<Element>();
// Step 1.
- let element_not_rendered = !node.is_in_doc() || !element.has_css_layout_box();
+ let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
if element_not_rendered {
return node.GetTextContent().unwrap();
}
@@ -707,7 +707,7 @@ impl HTMLElement {
let root_element = element.root_element();
let root_node = root_element.upcast::<Node>();
let children = root_node
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.filter(|elem| elem.is::<HTMLLabelElement>())
.filter(|elem| elem.get_string_attribute(&local_name!("for")) == id)
@@ -740,9 +740,9 @@ impl VirtualMethods for HTMLElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.update_sequentially_focusable_status();
}
diff --git a/components/script/dom/htmlfieldsetelement.rs b/components/script/dom/htmlfieldsetelement.rs
index d8e92180e56..4f97f7e66c7 100644
--- a/components/script/dom/htmlfieldsetelement.rs
+++ b/components/script/dom/htmlfieldsetelement.rs
@@ -13,7 +13,7 @@ use crate::dom::htmlcollection::{CollectionFilter, HTMLCollection};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
use crate::dom::htmllegendelement::HTMLLegendElement;
-use crate::dom::node::{window_from_node, Node};
+use crate::dom::node::{window_from_node, Node, ShadowIncluding};
use crate::dom::validitystate::ValidityState;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@@ -128,7 +128,7 @@ impl VirtualMethods for HTMLFieldSetElement {
});
let fields = children.flat_map(|child| {
child
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter(|descendant| match descendant.type_id() {
NodeTypeId::Element(ElementTypeId::HTMLElement(
HTMLElementTypeId::HTMLButtonElement,
diff --git a/components/script/dom/htmlformelement.rs b/components/script/dom/htmlformelement.rs
index 31087dc11df..9e988515ac0 100755
--- a/components/script/dom/htmlformelement.rs
+++ b/components/script/dom/htmlformelement.rs
@@ -41,7 +41,8 @@ use crate::dom::htmloutputelement::HTMLOutputElement;
use crate::dom::htmlselectelement::HTMLSelectElement;
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{Node, NodeFlags, UnbindContext, VecPreOrderInsertionHelper};
+use crate::dom::node::{Node, NodeFlags, ShadowIncluding};
+use crate::dom::node::{UnbindContext, VecPreOrderInsertionHelper};
use crate::dom::validitystate::ValidationFlags;
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::Window;
@@ -582,7 +583,7 @@ impl HTMLFormElement {
// form, refactor this when html5ever's form owner PR lands
// Step 1-3
let invalid_controls = node
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(|field| {
if let Some(el) = field.downcast::<Element>() {
if el.disabled_state() {
@@ -1100,7 +1101,7 @@ pub trait FormControl: DomObject {
let form_id = elem.get_string_attribute(&local_name!("form"));
let node = elem.upcast::<Node>();
- if self.is_listed() && !form_id.is_empty() && node.is_in_doc() {
+ if self.is_listed() && !form_id.is_empty() && node.is_connected() {
let doc = document_from_node(node);
doc.register_form_id_listener(form_id, self);
}
diff --git a/components/script/dom/htmlheadelement.rs b/components/script/dom/htmlheadelement.rs
index 5daf82fd896..0389ff9b43b 100644
--- a/components/script/dom/htmlheadelement.rs
+++ b/components/script/dom/htmlheadelement.rs
@@ -10,7 +10,7 @@ use crate::dom::document::{determine_policy_for_token, Document};
use crate::dom::element::Element;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlmetaelement::HTMLMetaElement;
-use crate::dom::node::{document_from_node, Node};
+use crate::dom::node::{document_from_node, BindContext, Node, ShadowIncluding};
use crate::dom::userscripts::load_script;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@@ -55,7 +55,7 @@ impl HTMLHeadElement {
let node = self.upcast::<Node>();
let candidates = node
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<Element>)
.filter(|elem| elem.is::<HTMLMetaElement>())
.filter(|elem| elem.get_string_attribute(&local_name!("name")) == "referrer")
@@ -81,9 +81,9 @@ impl VirtualMethods for HTMLHeadElement {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
load_script(self);
}
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs
index 24cea710450..a85b9bfa915 100644
--- a/components/script/dom/htmliframeelement.rs
+++ b/components/script/dom/htmliframeelement.rs
@@ -19,7 +19,9 @@ use crate::dom::element::{AttributeMutation, Element, RawLayoutElementHelpers};
use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext};
+use crate::dom::node::{
+ document_from_node, window_from_node, BindContext, Node, NodeDamage, UnbindContext,
+};
use crate::dom::virtualmethods::VirtualMethods;
use crate::dom::window::ReflowReason;
use crate::dom::windowproxy::WindowProxy;
@@ -584,7 +586,7 @@ impl VirtualMethods for HTMLIFrameElement {
// may be in a different script thread. Instread, we check to see if the parent
// is in a document tree and has a browsing context, which is what causes
// the child browsing context to be created.
- if self.upcast::<Node>().is_in_doc_with_browsing_context() {
+ if self.upcast::<Node>().is_connected_with_browsing_context() {
debug!("iframe src set while in browsing context.");
self.process_the_iframe_attributes(ProcessingMode::NotFirstTime);
}
@@ -610,11 +612,12 @@ impl VirtualMethods for HTMLIFrameElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
+ let tree_connected = context.tree_connected;
let iframe = Trusted::new(self);
document_from_node(self).add_delayed_task(task!(IFrameDelayedInitialize: move || {
let this = iframe.root();
@@ -624,9 +627,9 @@ impl VirtualMethods for HTMLIFrameElement {
// browsing context, set the element's nested browsing context
// to the newly-created browsing context, and then process the
// iframe attributes for the "first time"."
- if this.upcast::<Node>().is_in_doc_with_browsing_context() {
+ if this.upcast::<Node>().is_connected_with_browsing_context() {
debug!("iframe bound to browsing context.");
- debug_assert!(tree_in_doc, "is_in_doc_with_bc, but not tree_in_doc");
+ debug_assert!(tree_connected, "is_connected_with_bc, but not tree_connected");
this.create_nested_browsing_context();
this.process_the_iframe_attributes(ProcessingMode::FirstTime);
}
diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs
index 3848b3d7d2a..62a369579a1 100644
--- a/components/script/dom/htmlimageelement.rs
+++ b/components/script/dom/htmlimageelement.rs
@@ -32,7 +32,10 @@ use crate::dom::htmlmapelement::HTMLMapElement;
use crate::dom::htmlpictureelement::HTMLPictureElement;
use crate::dom::htmlsourceelement::HTMLSourceElement;
use crate::dom::mouseevent::MouseEvent;
-use crate::dom::node::{document_from_node, window_from_node, Node, NodeDamage, UnbindContext};
+use crate::dom::node::UnbindContext;
+use crate::dom::node::{
+ document_from_node, window_from_node, BindContext, Node, NodeDamage, ShadowIncluding,
+};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::progressevent::ProgressEvent;
use crate::dom::values::UNSIGNED_LONG_MAX;
@@ -1259,7 +1262,7 @@ impl HTMLImageElement {
let useMapElements = document_from_node(self)
.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLMapElement>)
.find(|n| {
n.upcast::<Element>()
@@ -1645,12 +1648,12 @@ impl VirtualMethods for HTMLImageElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
let document = document_from_node(self);
- if tree_in_doc {
+ if context.tree_connected {
document.register_responsive_image(self);
}
diff --git a/components/script/dom/htmlinputelement.rs b/components/script/dom/htmlinputelement.rs
index 94ffadb69fd..e9a3923b5d0 100755
--- a/components/script/dom/htmlinputelement.rs
+++ b/components/script/dom/htmlinputelement.rs
@@ -35,7 +35,7 @@ use crate::dom::htmlformelement::{ResetFrom, SubmittedFrom};
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::mouseevent::MouseEvent;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{Node, NodeDamage, UnbindContext};
+use crate::dom::node::{BindContext, Node, NodeDamage, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
use crate::dom::validation::Validatable;
@@ -1422,9 +1422,9 @@ impl VirtualMethods for HTMLInputElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.upcast::<Element>()
.check_ancestors_disabled_state_for_form_control();
diff --git a/components/script/dom/htmllabelelement.rs b/components/script/dom/htmllabelelement.rs
index 3c9106a589a..11ee09bce94 100644
--- a/components/script/dom/htmllabelelement.rs
+++ b/components/script/dom/htmllabelelement.rs
@@ -15,7 +15,7 @@ use crate::dom::event::Event;
use crate::dom::eventtarget::EventTarget;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlformelement::{FormControl, FormControlElementHelpers, HTMLFormElement};
-use crate::dom::node::{document_from_node, Node};
+use crate::dom::node::{document_from_node, Node, ShadowIncluding};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -162,7 +162,7 @@ impl VirtualMethods for HTMLLabelElement {
impl HTMLLabelElement {
pub fn first_labelable_descendant(&self) -> Option<DomRoot<HTMLElement>> {
self.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLElement>)
.filter(|elem| elem.is_labelable_element())
.next()
diff --git a/components/script/dom/htmllegendelement.rs b/components/script/dom/htmllegendelement.rs
index b69a066d9a1..41cf7d3d8ff 100644
--- a/components/script/dom/htmllegendelement.rs
+++ b/components/script/dom/htmllegendelement.rs
@@ -12,7 +12,7 @@ use crate::dom::element::Element;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
-use crate::dom::node::{Node, UnbindContext};
+use crate::dom::node::{BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -56,9 +56,9 @@ impl VirtualMethods for HTMLLegendElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.upcast::<Element>()
diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs
index 36bafa58fb5..8dd50c11441 100644
--- a/components/script/dom/htmllinkelement.rs
+++ b/components/script/dom/htmllinkelement.rs
@@ -18,7 +18,10 @@ use crate::dom::element::{
};
use crate::dom::element::{AttributeMutation, Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::{document_from_node, window_from_node, Node, UnbindContext};
+use crate::dom::node::{
+ document_from_node, stylesheets_owner_from_node, window_from_node, BindContext, Node,
+ UnbindContext,
+};
use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
use crate::dom::virtualmethods::VirtualMethods;
use crate::stylesheet_loader::{StylesheetContextSource, StylesheetLoader, StylesheetOwner};
@@ -107,14 +110,15 @@ impl HTMLLinkElement {
// FIXME(emilio): These methods are duplicated with
// HTMLStyleElement::set_stylesheet.
+ #[allow(unrooted_must_root)]
pub fn set_stylesheet(&self, s: Arc<Stylesheet>) {
- let doc = document_from_node(self);
+ let stylesheets_owner = stylesheets_owner_from_node(self);
if let Some(ref s) = *self.stylesheet.borrow() {
- doc.remove_stylesheet(self.upcast(), s)
+ stylesheets_owner.remove_stylesheet(self.upcast(), s)
}
*self.stylesheet.borrow_mut() = Some(s.clone());
self.cssom_stylesheet.set(None);
- doc.add_stylesheet(self.upcast(), s);
+ stylesheets_owner.add_stylesheet(self.upcast(), s);
}
pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
@@ -183,7 +187,7 @@ impl VirtualMethods for HTMLLinkElement {
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
- if !self.upcast::<Node>().is_in_doc() || mutation.is_removal() {
+ if !self.upcast::<Node>().is_connected() || mutation.is_removal() {
return;
}
@@ -222,12 +226,12 @@ impl VirtualMethods for HTMLLinkElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
- if tree_in_doc {
+ if context.tree_connected {
let element = self.upcast();
let rel = get_attr(element, &local_name!("rel"));
@@ -252,7 +256,7 @@ impl VirtualMethods for HTMLLinkElement {
}
if let Some(s) = self.stylesheet.borrow_mut().take() {
- document_from_node(self).remove_stylesheet(self.upcast(), &s);
+ stylesheets_owner_from_node(self).remove_stylesheet(self.upcast(), &s);
}
}
}
diff --git a/components/script/dom/htmlmapelement.rs b/components/script/dom/htmlmapelement.rs
index 34b73e7e0b9..0b6acb3ba31 100644
--- a/components/script/dom/htmlmapelement.rs
+++ b/components/script/dom/htmlmapelement.rs
@@ -8,7 +8,7 @@ use crate::dom::bindings::root::DomRoot;
use crate::dom::document::Document;
use crate::dom::htmlareaelement::HTMLAreaElement;
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::Node;
+use crate::dom::node::{Node, ShadowIncluding};
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -43,7 +43,7 @@ impl HTMLMapElement {
pub fn get_area_elements(&self) -> Vec<DomRoot<HTMLAreaElement>> {
self.upcast::<Node>()
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLAreaElement>)
.collect()
}
diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs
index 1b5f991ac8e..74c7c7ade62 100644
--- a/components/script/dom/htmlmediaelement.rs
+++ b/components/script/dom/htmlmediaelement.rs
@@ -2009,7 +2009,7 @@ impl VirtualMethods for HTMLMediaElement {
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
- if context.tree_in_doc {
+ if context.tree_connected {
let task = MediaElementMicrotask::PauseIfNotInDocumentTask {
elem: DomRoot::from_ref(self),
};
@@ -2061,7 +2061,7 @@ impl MicrotaskRunnable for MediaElementMicrotask {
}
},
&MediaElementMicrotask::PauseIfNotInDocumentTask { ref elem } => {
- if !elem.upcast::<Node>().is_in_doc() {
+ if !elem.upcast::<Node>().is_connected() {
elem.internal_pause_steps();
}
},
diff --git a/components/script/dom/htmlmetaelement.rs b/components/script/dom/htmlmetaelement.rs
index a8e829c3d1c..11b5706516d 100644
--- a/components/script/dom/htmlmetaelement.rs
+++ b/components/script/dom/htmlmetaelement.rs
@@ -15,7 +15,10 @@ use crate::dom::document::Document;
use crate::dom::element::{AttributeMutation, Element};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlheadelement::HTMLHeadElement;
-use crate::dom::node::{document_from_node, window_from_node, Node, UnbindContext};
+use crate::dom::node::{
+ document_from_node, stylesheets_owner_from_node, window_from_node, BindContext, Node,
+ UnbindContext,
+};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -97,6 +100,7 @@ impl HTMLMetaElement {
}
}
+ #[allow(unrooted_must_root)]
fn apply_viewport(&self) {
if !pref!(layout.viewport.enabled) {
return;
@@ -106,6 +110,7 @@ impl HTMLMetaElement {
let content = content.value();
if !content.is_empty() {
if let Some(translated_rule) = ViewportRule::from_meta(&**content) {
+ let stylesheets_owner = stylesheets_owner_from_node(self);
let document = document_from_node(self);
let shared_lock = document.style_shared_lock();
let rule = CssRule::Viewport(Arc::new(shared_lock.wrap(translated_rule)));
@@ -124,7 +129,7 @@ impl HTMLMetaElement {
disabled: AtomicBool::new(false),
});
*self.stylesheet.borrow_mut() = Some(sheet.clone());
- document.add_stylesheet(self.upcast(), sheet);
+ stylesheets_owner.add_stylesheet(self.upcast(), sheet);
}
}
}
@@ -171,12 +176,12 @@ impl VirtualMethods for HTMLMetaElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
- if tree_in_doc {
+ if context.tree_connected {
self.process_attributes();
}
}
@@ -204,11 +209,11 @@ impl VirtualMethods for HTMLMetaElement {
s.unbind_from_tree(context);
}
- if context.tree_in_doc {
+ if context.tree_connected {
self.process_referrer_attribute();
if let Some(s) = self.stylesheet.borrow_mut().take() {
- document_from_node(self).remove_stylesheet(self.upcast(), &s);
+ stylesheets_owner_from_node(self).remove_stylesheet(self.upcast(), &s);
}
}
}
diff --git a/components/script/dom/htmloptionelement.rs b/components/script/dom/htmloptionelement.rs
index 494ff7b0a8c..3893aea95ab 100644
--- a/components/script/dom/htmloptionelement.rs
+++ b/components/script/dom/htmloptionelement.rs
@@ -19,7 +19,7 @@ use crate::dom::htmlformelement::HTMLFormElement;
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
use crate::dom::htmlscriptelement::HTMLScriptElement;
use crate::dom::htmlselectelement::HTMLSelectElement;
-use crate::dom::node::{Node, UnbindContext};
+use crate::dom::node::{BindContext, Node, ShadowIncluding, UnbindContext};
use crate::dom::text::Text;
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
@@ -235,9 +235,9 @@ impl VirtualMethods for HTMLOptionElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.upcast::<Element>()
@@ -251,7 +251,7 @@ impl VirtualMethods for HTMLOptionElement {
if let Some(select) = context
.parent
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLSelectElement>)
.next()
{
diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs
index 627cfc7e779..8d44c4b9ffd 100644
--- a/components/script/dom/htmlscriptelement.rs
+++ b/components/script/dom/htmlscriptelement.rs
@@ -22,7 +22,7 @@ use crate::dom::event::{Event, EventBubbles, EventCancelable, EventStatus};
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node};
+use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node};
use crate::dom::performanceresourcetiming::InitiatorType;
use crate::dom::virtualmethods::VirtualMethods;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
@@ -372,7 +372,7 @@ impl HTMLScriptElement {
}
// Step 5.
- if !self.upcast::<Node>().is_in_doc() {
+ if !self.upcast::<Node>().is_connected() {
return;
}
@@ -760,7 +760,7 @@ impl VirtualMethods for HTMLScriptElement {
match *attr.local_name() {
local_name!("src") => {
if let AttributeMutation::Set(_) = mutation {
- if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
+ if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
@@ -773,17 +773,17 @@ impl VirtualMethods for HTMLScriptElement {
if let Some(ref s) = self.super_type() {
s.children_changed(mutation);
}
- if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
+ if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
- if tree_in_doc && !self.parser_inserted.get() {
+ if context.tree_connected && !self.parser_inserted.get() {
let script = Trusted::new(self);
document_from_node(self).add_delayed_task(task!(ScriptDelayedInitialize: move || {
script.root().prepare();
diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs
index 50d88710161..c71a5442e73 100755
--- a/components/script/dom/htmlselectelement.rs
+++ b/components/script/dom/htmlselectelement.rs
@@ -12,7 +12,6 @@ use crate::dom::bindings::codegen::Bindings::HTMLSelectElementBinding::HTMLSelec
use crate::dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use crate::dom::bindings::codegen::UnionTypes::HTMLElementOrLong;
use crate::dom::bindings::codegen::UnionTypes::HTMLOptionElementOrHTMLOptGroupElement;
-//use dom::bindings::error::ErrorResult;
use crate::dom::bindings::inheritance::Castable;
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
use crate::dom::bindings::str::DOMString;
@@ -25,7 +24,7 @@ use crate::dom::htmlformelement::{FormControl, FormDatum, FormDatumValue, HTMLFo
use crate::dom::htmloptgroupelement::HTMLOptGroupElement;
use crate::dom::htmloptionelement::HTMLOptionElement;
use crate::dom::htmloptionscollection::HTMLOptionsCollection;
-use crate::dom::node::{window_from_node, Node, UnbindContext};
+use crate::dom::node::{window_from_node, BindContext, Node, UnbindContext};
use crate::dom::nodelist::NodeList;
use crate::dom::validation::Validatable;
use crate::dom::validitystate::{ValidationFlags, ValidityState};
@@ -382,9 +381,9 @@ impl VirtualMethods for HTMLSelectElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.upcast::<Element>()
diff --git a/components/script/dom/htmlsourceelement.rs b/components/script/dom/htmlsourceelement.rs
index 783bcde4c80..f93de93599d 100644
--- a/components/script/dom/htmlsourceelement.rs
+++ b/components/script/dom/htmlsourceelement.rs
@@ -15,7 +15,7 @@ use crate::dom::element::AttributeMutation;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlmediaelement::HTMLMediaElement;
-use crate::dom::node::{Node, UnbindContext};
+use crate::dom::node::{BindContext, Node, UnbindContext};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -82,8 +82,8 @@ impl VirtualMethods for HTMLSourceElement {
}
/// <https://html.spec.whatwg.org/multipage/#the-source-element:nodes-are-inserted>
- fn bind_to_tree(&self, tree_in_doc: bool) {
- self.super_type().unwrap().bind_to_tree(tree_in_doc);
+ fn bind_to_tree(&self, context: &BindContext) {
+ self.super_type().unwrap().bind_to_tree(context);
let parent = self.upcast::<Node>().GetParentNode().unwrap();
if let Some(media) = parent.downcast::<HTMLMediaElement>() {
media.handle_source_child_insertion();
diff --git a/components/script/dom/htmlstyleelement.rs b/components/script/dom/htmlstyleelement.rs
index b4065d205a8..e483453252a 100644
--- a/components/script/dom/htmlstyleelement.rs
+++ b/components/script/dom/htmlstyleelement.rs
@@ -13,7 +13,8 @@ use crate::dom::document::Document;
use crate::dom::element::{Element, ElementCreator};
use crate::dom::htmlelement::HTMLElement;
use crate::dom::node::{
- document_from_node, window_from_node, ChildrenMutation, Node, UnbindContext,
+ document_from_node, stylesheets_owner_from_node, window_from_node, BindContext,
+ ChildrenMutation, Node, UnbindContext,
};
use crate::dom::stylesheet::StyleSheet as DOMStyleSheet;
use crate::dom::virtualmethods::VirtualMethods;
@@ -81,7 +82,7 @@ impl HTMLStyleElement {
pub fn parse_own_css(&self) {
let node = self.upcast::<Node>();
let element = self.upcast::<Element>();
- assert!(node.is_in_doc());
+ assert!(node.is_connected());
let window = window_from_node(node);
let doc = document_from_node(self);
@@ -137,14 +138,15 @@ impl HTMLStyleElement {
}
// FIXME(emilio): This is duplicated with HTMLLinkElement::set_stylesheet.
+ #[allow(unrooted_must_root)]
pub fn set_stylesheet(&self, s: Arc<Stylesheet>) {
- let doc = document_from_node(self);
+ let stylesheets_owner = stylesheets_owner_from_node(self);
if let Some(ref s) = *self.stylesheet.borrow() {
- doc.remove_stylesheet(self.upcast(), s)
+ stylesheets_owner.remove_stylesheet(self.upcast(), s)
}
*self.stylesheet.borrow_mut() = Some(s.clone());
self.cssom_stylesheet.set(None);
- doc.add_stylesheet(self.upcast(), s);
+ stylesheets_owner.add_stylesheet(self.upcast(), s);
}
pub fn get_stylesheet(&self) -> Option<Arc<Stylesheet>> {
@@ -185,14 +187,14 @@ impl VirtualMethods for HTMLStyleElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
- self.super_type().unwrap().bind_to_tree(tree_in_doc);
+ fn bind_to_tree(&self, context: &BindContext) {
+ self.super_type().unwrap().bind_to_tree(context);
// https://html.spec.whatwg.org/multipage/#update-a-style-block
// Handles the case when:
// "The element is not on the stack of open elements of an HTML parser or XML parser,
// and it becomes connected or disconnected."
- if tree_in_doc && !self.in_stack_of_open_elements.get() {
+ if context.tree_connected && !self.in_stack_of_open_elements.get() {
self.parse_own_css();
}
}
@@ -214,9 +216,9 @@ impl VirtualMethods for HTMLStyleElement {
s.unbind_from_tree(context);
}
- if context.tree_in_doc {
+ if context.tree_connected {
if let Some(s) = self.stylesheet.borrow_mut().take() {
- document_from_node(self).remove_stylesheet(self.upcast(), &s)
+ stylesheets_owner_from_node(self).remove_stylesheet(self.upcast(), &s)
}
}
}
diff --git a/components/script/dom/htmltextareaelement.rs b/components/script/dom/htmltextareaelement.rs
index 9116b26b575..34dd94b7a36 100755
--- a/components/script/dom/htmltextareaelement.rs
+++ b/components/script/dom/htmltextareaelement.rs
@@ -24,7 +24,9 @@ use crate::dom::htmlfieldsetelement::HTMLFieldSetElement;
use crate::dom::htmlformelement::{FormControl, HTMLFormElement};
use crate::dom::keyboardevent::KeyboardEvent;
use crate::dom::node::{document_from_node, window_from_node};
-use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext};
+use crate::dom::node::{
+ BindContext, ChildrenMutation, CloneChildrenFlag, Node, NodeDamage, UnbindContext,
+};
use crate::dom::nodelist::NodeList;
use crate::dom::textcontrol::{TextControlElement, TextControlSelection};
use crate::dom::validation::Validatable;
@@ -466,9 +468,9 @@ impl VirtualMethods for HTMLTextAreaElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
self.upcast::<Element>()
diff --git a/components/script/dom/htmltitleelement.rs b/components/script/dom/htmltitleelement.rs
index 1aa60373df6..a1e543d34e2 100644
--- a/components/script/dom/htmltitleelement.rs
+++ b/components/script/dom/htmltitleelement.rs
@@ -10,7 +10,7 @@ use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::document::Document;
use crate::dom::htmlelement::HTMLElement;
-use crate::dom::node::{ChildrenMutation, Node};
+use crate::dom::node::{BindContext, ChildrenMutation, Node};
use crate::dom::virtualmethods::VirtualMethods;
use dom_struct::dom_struct;
use html5ever::{LocalName, Prefix};
@@ -74,12 +74,12 @@ impl VirtualMethods for HTMLTitleElement {
}
}
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
let node = self.upcast::<Node>();
- if tree_in_doc {
+ if context.tree_in_doc {
node.owner_doc().title_changed();
}
}
diff --git a/components/script/dom/macros.rs b/components/script/dom/macros.rs
index a566fee4fb6..021527a286a 100644
--- a/components/script/dom/macros.rs
+++ b/components/script/dom/macros.rs
@@ -632,3 +632,31 @@ macro_rules! handle_potential_webgl_error {
handle_potential_webgl_error!($context, $call, ());
};
}
+
+macro_rules! impl_rare_data (
+ ($type:ty) => (
+ fn rare_data(&self) -> Ref<Option<Box<$type>>> {
+ self.rare_data.borrow()
+ }
+
+ #[allow(dead_code)]
+ fn rare_data_mut(&self) -> RefMut<Option<Box<$type>>> {
+ self.rare_data.borrow_mut()
+ }
+
+ fn ensure_rare_data(&self) -> RefMut<Box<$type>> {
+ let mut rare_data = self.rare_data.borrow_mut();
+ if rare_data.is_none() {
+ *rare_data = Some(Default::default());
+ }
+ RefMut::map(rare_data, |rare_data| {
+ rare_data.as_mut().unwrap()
+ })
+ }
+
+ #[allow(unsafe_code)]
+ fn rare_data_for_layout(&self) -> &Option<Box<$type>> {
+ unsafe { self.rare_data.borrow_for_layout() }
+ }
+ );
+);
diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs
index 813f4659a08..776d20299c1 100644
--- a/components/script/dom/mod.rs
+++ b/components/script/dom/mod.rs
@@ -280,6 +280,7 @@ pub mod dissimilaroriginlocation;
pub mod dissimilaroriginwindow;
pub mod document;
pub mod documentfragment;
+pub mod documentorshadowroot;
pub mod documenttype;
pub mod domexception;
pub mod domimplementation;
@@ -442,6 +443,7 @@ pub mod promisenativehandler;
pub mod promiserejectionevent;
pub mod radionodelist;
pub mod range;
+pub mod raredata;
pub mod request;
pub mod response;
pub mod rtcicecandidate;
@@ -454,6 +456,7 @@ pub mod serviceworkercontainer;
pub mod serviceworkerglobalscope;
pub mod serviceworkerregistration;
pub mod servoparser;
+pub mod shadowroot;
pub mod storage;
pub mod storageevent;
pub mod stylepropertymapreadonly;
diff --git a/components/script/dom/mutationobserver.rs b/components/script/dom/mutationobserver.rs
index b25f99d0861..bb87a10f4b2 100644
--- a/components/script/dom/mutationobserver.rs
+++ b/components/script/dom/mutationobserver.rs
@@ -13,7 +13,7 @@ use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, Reflector};
use crate::dom::bindings::root::DomRoot;
use crate::dom::bindings::str::DOMString;
use crate::dom::mutationrecord::MutationRecord;
-use crate::dom::node::Node;
+use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::window::Window;
use crate::microtask::Microtask;
use crate::script_thread::ScriptThread;
@@ -131,7 +131,7 @@ impl MutationObserver {
let mut interested_observers: Vec<(DomRoot<MutationObserver>, Option<DOMString>)> = vec![];
// Step 2 & 3
- for node in target.inclusive_ancestors() {
+ for node in target.inclusive_ancestors(ShadowIncluding::No) {
for registered in &*node.registered_mutation_observers() {
if &*node != target && !registered.options.subtree {
continue;
@@ -318,20 +318,18 @@ impl MutationObserverMethods for MutationObserver {
// Step 8
if add_new_observer {
- target
- .registered_mutation_observers()
- .push(RegisteredObserver {
- observer: DomRoot::from_ref(self),
- options: ObserverOptions {
- attributes,
- attribute_old_value,
- character_data,
- character_data_old_value,
- subtree,
- attribute_filter,
- child_list,
- },
- });
+ target.add_mutation_observer(RegisteredObserver {
+ observer: DomRoot::from_ref(self),
+ options: ObserverOptions {
+ attributes,
+ attribute_old_value,
+ character_data,
+ character_data_old_value,
+ subtree,
+ attribute_filter,
+ child_list,
+ },
+ });
self.node_list.borrow_mut().push(DomRoot::from_ref(target));
}
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 3331686eef4..814378aca88 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -10,10 +10,14 @@ use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterData
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
-use crate::dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
+use crate::dom::bindings::codegen::Bindings::NodeBinding::{
+ GetRootNodeOptions, NodeConstants, NodeMethods,
+};
use crate::dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
use crate::dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
+use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootBinding::ShadowRootMethods;
use crate::dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use crate::dom::bindings::codegen::InheritTypes::DocumentFragmentTypeId;
use crate::dom::bindings::codegen::UnionTypes::NodeOrString;
use crate::dom::bindings::conversions::{self, DerivedFrom};
use crate::dom::bindings::error::{Error, ErrorResult, Fallible};
@@ -49,6 +53,9 @@ use crate::dom::mutationobserver::{Mutation, MutationObserver, RegisteredObserve
use crate::dom::nodelist::NodeList;
use crate::dom::processinginstruction::ProcessingInstruction;
use crate::dom::range::WeakRangeVec;
+use crate::dom::raredata::NodeRareData;
+use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot};
+use crate::dom::stylesheetlist::StyleSheetListOwner;
use crate::dom::svgsvgelement::{LayoutSVGSVGElementHelpers, SVGSVGElement};
use crate::dom::text::Text;
use crate::dom::virtualmethods::{vtable_for, VirtualMethods};
@@ -76,7 +83,7 @@ use servo_arc::Arc;
use servo_url::ServoUrl;
use smallvec::SmallVec;
use std::borrow::ToOwned;
-use std::cell::{Cell, RefMut, UnsafeCell};
+use std::cell::{Cell, Ref, RefMut, UnsafeCell};
use std::cmp;
use std::default::Default;
use std::iter;
@@ -118,6 +125,9 @@ pub struct Node {
/// The document that this node belongs to.
owner_doc: MutNullableDom<Document>,
+ /// Rare node data.
+ rare_data: DomRefCell<Option<Box<NodeRareData>>>,
+
/// The live list of children return by .childNodes.
child_list: MutNullableDom<NodeList>,
@@ -142,9 +152,6 @@ pub struct Node {
/// node is finalized.
style_and_layout_data: Cell<Option<OpaqueStyleAndLayoutData>>,
- /// Registered observers for this node.
- mutation_observers: DomRefCell<Vec<RegisteredObserver>>,
-
unique_id: UniqueId,
}
@@ -181,6 +188,12 @@ bitflags! {
/// Whether this element has already handled the stored snapshot.
const HANDLED_SNAPSHOT = 1 << 8;
+
+ /// Whether this node participates in a shadow tree.
+ const IS_IN_SHADOW_TREE = 1 << 9;
+
+ /// Specifies whether this node's shadow-including root is a document.
+ const IS_CONNECTED = 1 << 10;
}
}
@@ -264,11 +277,25 @@ impl Node {
self.children_count.set(self.children_count.get() + 1);
let parent_in_doc = self.is_in_doc();
- for node in new_child.traverse_preorder() {
+ let parent_in_shadow_tree = self.is_in_shadow_tree();
+ let parent_is_connected = self.is_connected();
+
+ for node in new_child.traverse_preorder(ShadowIncluding::No) {
+ if parent_in_shadow_tree {
+ if let Some(shadow_root) = self.containing_shadow_root() {
+ node.set_containing_shadow_root(&*shadow_root);
+ }
+ debug_assert!(node.containing_shadow_root().is_some());
+ }
node.set_flag(NodeFlags::IS_IN_DOC, parent_in_doc);
+ node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, parent_in_shadow_tree);
+ node.set_flag(NodeFlags::IS_CONNECTED, parent_is_connected);
// Out-of-document elements never have the descendants flag set.
debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
- vtable_for(&&*node).bind_to_tree(parent_in_doc);
+ vtable_for(&&*node).bind_to_tree(&BindContext {
+ tree_connected: parent_is_connected,
+ tree_in_doc: parent_in_doc,
+ });
}
}
@@ -312,17 +339,18 @@ impl Node {
child.parent_node.set(None);
self.children_count.set(self.children_count.get() - 1);
- for node in child.traverse_preorder() {
+ for node in child.traverse_preorder(ShadowIncluding::Yes) {
// Out-of-document elements never have the descendants flag set.
node.set_flag(
NodeFlags::IS_IN_DOC |
+ NodeFlags::IS_CONNECTED |
NodeFlags::HAS_DIRTY_DESCENDANTS |
NodeFlags::HAS_SNAPSHOT |
NodeFlags::HANDLED_SNAPSHOT,
false,
);
}
- for node in child.traverse_preorder() {
+ for node in child.traverse_preorder(ShadowIncluding::Yes) {
// This needs to be in its own loop, because unbind_from_tree may
// rely on the state of IS_IN_DOC of the context node's descendants,
// e.g. when removing a <form>.
@@ -403,6 +431,8 @@ impl<'a> Iterator for QuerySelectorIterator {
}
impl Node {
+ impl_rare_data!(NodeRareData);
+
pub fn teardown(&self) {
self.style_and_layout_data.get().map(|d| self.dispose(d));
for kid in self.children() {
@@ -422,14 +452,26 @@ impl Node {
}
/// Return all registered mutation observers for this node.
+ /// XXX(ferjm) This should probably be split into two functions,
+ /// `registered_mutation_observers`, which returns an Option or
+ /// an empty slice or something, and doesn't create the rare data,
+ /// and `registered_mutation_observers_mut`, which does lazily
+ /// initialize the raredata.
pub fn registered_mutation_observers(&self) -> RefMut<Vec<RegisteredObserver>> {
- self.mutation_observers.borrow_mut()
+ RefMut::map(self.ensure_rare_data(), |rare_data| {
+ &mut rare_data.mutation_observers
+ })
+ }
+
+ /// Add a new mutation observer for a given node.
+ pub fn add_mutation_observer(&self, observer: RegisteredObserver) {
+ self.ensure_rare_data().mutation_observers.push(observer);
}
/// Removes the mutation observer for a given node.
pub fn remove_mutation_observer(&self, observer: &MutationObserver) {
- self.mutation_observers
- .borrow_mut()
+ self.ensure_rare_data()
+ .mutation_observers
.retain(|reg_obs| &*reg_obs.observer != observer)
}
@@ -463,6 +505,14 @@ impl Node {
self.flags.get().contains(NodeFlags::IS_IN_DOC)
}
+ pub fn is_in_shadow_tree(&self) -> bool {
+ self.flags.get().contains(NodeFlags::IS_IN_SHADOW_TREE)
+ }
+
+ pub fn is_connected(&self) -> bool {
+ self.flags.get().contains(NodeFlags::IS_CONNECTED)
+ }
+
/// Returns the type ID of this node.
pub fn type_id(&self) -> NodeTypeId {
match *self.eventtarget.type_id() {
@@ -521,14 +571,16 @@ impl Node {
// FIXME(emilio): This and the function below should move to Element.
pub fn note_dirty_descendants(&self) {
- debug_assert!(self.is_in_doc());
+ debug_assert!(self.is_connected());
- for ancestor in self.inclusive_ancestors() {
+ for ancestor in self.inclusive_ancestors(ShadowIncluding::Yes) {
if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
return;
}
- ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
+ if ancestor.is::<Element>() {
+ ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
+ }
}
}
@@ -546,7 +598,7 @@ impl Node {
self.inclusive_descendants_version(),
doc.inclusive_descendants_version(),
) + 1;
- for ancestor in self.inclusive_ancestors() {
+ for ancestor in self.inclusive_ancestors(ShadowIncluding::No) {
ancestor.inclusive_descendants_version.set(version);
}
doc.inclusive_descendants_version.set(version);
@@ -554,19 +606,21 @@ impl Node {
pub fn dirty(&self, damage: NodeDamage) {
self.rev_version();
- if !self.is_in_doc() {
+ if !self.is_connected() {
return;
}
match self.type_id() {
- NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => self
- .parent_node
- .get()
- .unwrap()
- .downcast::<Element>()
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
+ self.parent_node.get().unwrap().dirty(damage)
+ },
+ NodeTypeId::Element(_) => self.downcast::<Element>().unwrap().restyle(damage),
+ NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot) => self
+ .downcast::<ShadowRoot>()
.unwrap()
+ .Host()
+ .upcast::<Element>()
.restyle(damage),
- NodeTypeId::Element(_) => self.downcast::<Element>().unwrap().restyle(damage),
_ => {},
};
}
@@ -577,8 +631,8 @@ impl Node {
}
/// Iterates over this node and all its descendants, in preorder.
- pub fn traverse_preorder(&self) -> TreeIterator {
- TreeIterator::new(self)
+ pub fn traverse_preorder(&self, shadow_including: ShadowIncluding) -> TreeIterator {
+ TreeIterator::new(self, shadow_including)
}
pub fn inclusively_following_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
@@ -603,6 +657,11 @@ impl Node {
parent.ancestors().any(|ancestor| &*ancestor == self)
}
+ fn is_shadow_including_inclusive_ancestor_of(&self, node: &Node) -> bool {
+ node.inclusive_ancestors(ShadowIncluding::Yes)
+ .any(|ancestor| &*ancestor == self)
+ }
+
pub fn following_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
SimpleNodeIterator {
current: self.GetNextSibling(),
@@ -820,7 +879,7 @@ impl Node {
self.owner_doc().quirks_mode(),
);
Ok(self
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::No)
.filter_map(DomRoot::downcast)
.find(|element| matches_selector_list(&selectors, element, &mut ctx)))
},
@@ -838,7 +897,7 @@ impl Node {
Err(_) => Err(Error::Syntax),
// Step 3.
Ok(selectors) => {
- let mut descendants = self.traverse_preorder();
+ let mut descendants = self.traverse_preorder(ShadowIncluding::No);
// Skip the root of the tree.
assert!(&*descendants.next().unwrap() == self);
Ok(QuerySelectorIterator::new(descendants, selectors))
@@ -861,10 +920,21 @@ impl Node {
}
}
- pub fn inclusive_ancestors(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ /// https://dom.spec.whatwg.org/#concept-shadow-including-inclusive-ancestor
+ pub fn inclusive_ancestors(
+ &self,
+ shadow_including: ShadowIncluding,
+ ) -> impl Iterator<Item = DomRoot<Node>> {
SimpleNodeIterator {
current: Some(DomRoot::from_ref(self)),
- next_node: |n| n.GetParentNode(),
+ next_node: move |n| {
+ if shadow_including == ShadowIncluding::Yes {
+ if let Some(shadow_root) = n.downcast::<ShadowRoot>() {
+ return Some(DomRoot::from_ref(shadow_root.Host().upcast::<Node>()));
+ }
+ }
+ n.GetParentNode()
+ },
}
}
@@ -876,12 +946,24 @@ impl Node {
self.owner_doc.set(Some(document));
}
+ pub fn containing_shadow_root(&self) -> Option<DomRoot<ShadowRoot>> {
+ self.rare_data()
+ .as_ref()?
+ .containing_shadow_root
+ .as_ref()
+ .map(|sr| DomRoot::from_ref(&**sr))
+ }
+
+ pub fn set_containing_shadow_root(&self, shadow_root: &ShadowRoot) {
+ self.ensure_rare_data().containing_shadow_root = Some(Dom::from_ref(shadow_root));
+ }
+
pub fn is_in_html_doc(&self) -> bool {
self.owner_doc().is_html_document()
}
- pub fn is_in_doc_with_browsing_context(&self) -> bool {
- self.is_in_doc() && self.owner_doc().browsing_context().is_some()
+ pub fn is_connected_with_browsing_context(&self) -> bool {
+ self.is_connected() && self.owner_doc().browsing_context().is_some()
}
pub fn children(&self) -> impl Iterator<Item = DomRoot<Node>> {
@@ -1042,6 +1124,27 @@ impl Node {
None
}
}
+
+ /// https://dom.spec.whatwg.org/#retarget
+ pub fn retarget(&self, b: &Node) -> DomRoot<Node> {
+ let mut a = DomRoot::from_ref(&*self);
+ loop {
+ // Step 1.
+ let a_root = a.GetRootNode(&GetRootNodeOptions::empty());
+ if !a_root.is::<ShadowRoot>() || a_root.is_shadow_including_inclusive_ancestor_of(b) {
+ return DomRoot::from_ref(&a);
+ }
+
+ // Step 2.
+ a = DomRoot::from_ref(
+ a_root
+ .downcast::<ShadowRoot>()
+ .unwrap()
+ .Host()
+ .upcast::<Node>(),
+ );
+ }
+ }
}
/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
@@ -1080,13 +1183,14 @@ pub unsafe fn from_untrusted_node_address(
pub trait LayoutNodeHelpers {
unsafe fn type_id_for_layout(&self) -> NodeTypeId;
- unsafe fn parent_node_ref(&self) -> Option<LayoutDom<Node>>;
+ unsafe fn composed_parent_node_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn first_child_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn last_child_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn prev_sibling_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn next_sibling_ref(&self) -> Option<LayoutDom<Node>>;
unsafe fn owner_doc_for_layout(&self) -> LayoutDom<Document>;
+ unsafe fn containing_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>>;
unsafe fn is_element_for_layout(&self) -> bool;
unsafe fn get_flag(&self, flag: NodeFlags) -> bool;
@@ -1126,8 +1230,14 @@ impl LayoutNodeHelpers for LayoutDom<Node> {
#[inline]
#[allow(unsafe_code)]
- unsafe fn parent_node_ref(&self) -> Option<LayoutDom<Node>> {
- (*self.unsafe_get()).parent_node.get_inner_as_layout()
+ unsafe fn composed_parent_node_ref(&self) -> Option<LayoutDom<Node>> {
+ let parent = (*self.unsafe_get()).parent_node.get_inner_as_layout();
+ if let Some(ref parent) = parent {
+ if let Some(shadow_root) = parent.downcast::<ShadowRoot>() {
+ return Some(shadow_root.get_host_for_layout().upcast());
+ }
+ }
+ parent
}
#[inline]
@@ -1165,6 +1275,17 @@ impl LayoutNodeHelpers for LayoutDom<Node> {
#[inline]
#[allow(unsafe_code)]
+ unsafe fn containing_shadow_root_for_layout(&self) -> Option<LayoutDom<ShadowRoot>> {
+ (*self.unsafe_get())
+ .rare_data_for_layout()
+ .as_ref()?
+ .containing_shadow_root
+ .as_ref()
+ .map(|sr| sr.to_layout())
+ }
+
+ #[inline]
+ #[allow(unsafe_code)]
unsafe fn get_flag(&self, flag: NodeFlags) -> bool {
(*self.unsafe_get()).flags.get().contains(flag)
}
@@ -1325,7 +1446,7 @@ impl FollowingNodeIterator {
return current.GetNextSibling();
}
- for ancestor in current.inclusive_ancestors() {
+ for ancestor in current.inclusive_ancestors(ShadowIncluding::No) {
if self.root == ancestor {
break;
}
@@ -1405,16 +1526,25 @@ where
}
}
+/// Whether a tree traversal should pass shadow tree boundaries.
+#[derive(PartialEq)]
+pub enum ShadowIncluding {
+ No,
+ Yes,
+}
+
pub struct TreeIterator {
current: Option<DomRoot<Node>>,
depth: usize,
+ shadow_including: bool,
}
impl TreeIterator {
- fn new(root: &Node) -> TreeIterator {
+ fn new(root: &Node, shadow_including: ShadowIncluding) -> TreeIterator {
TreeIterator {
current: Some(DomRoot::from_ref(root)),
depth: 0,
+ shadow_including: shadow_including == ShadowIncluding::Yes,
}
}
@@ -1425,7 +1555,13 @@ impl TreeIterator {
}
fn next_skipping_children_impl(&mut self, current: DomRoot<Node>) -> Option<DomRoot<Node>> {
- for ancestor in current.inclusive_ancestors() {
+ let iter = current.inclusive_ancestors(if self.shadow_including {
+ ShadowIncluding::Yes
+ } else {
+ ShadowIncluding::No
+ });
+
+ for ancestor in iter {
if self.depth == 0 {
break;
}
@@ -1445,8 +1581,18 @@ impl Iterator for TreeIterator {
type Item = DomRoot<Node>;
// https://dom.spec.whatwg.org/#concept-tree-order
+ // https://dom.spec.whatwg.org/#concept-shadow-including-tree-order
fn next(&mut self) -> Option<DomRoot<Node>> {
let current = self.current.take()?;
+
+ if !self.shadow_including {
+ if let Some(element) = current.downcast::<Element>() {
+ if element.is_shadow_host() {
+ return self.next_skipping_children_impl(current);
+ }
+ }
+ }
+
if let Some(first_child) = current.GetFirstChild() {
self.current = Some(first_child);
self.depth += 1;
@@ -1487,7 +1633,10 @@ impl Node {
#[allow(unrooted_must_root)]
pub fn new_document_node() -> Node {
- Node::new_(NodeFlags::new() | NodeFlags::IS_IN_DOC, None)
+ Node::new_(
+ NodeFlags::new() | NodeFlags::IS_IN_DOC | NodeFlags::IS_CONNECTED,
+ None,
+ )
}
#[allow(unrooted_must_root)]
@@ -1501,6 +1650,7 @@ impl Node {
next_sibling: Default::default(),
prev_sibling: Default::default(),
owner_doc: MutNullableDom::new(doc),
+ rare_data: Default::default(),
child_list: Default::default(),
children_count: Cell::new(0u32),
flags: Cell::new(flags),
@@ -1509,8 +1659,6 @@ impl Node {
style_and_layout_data: Cell::new(None),
- mutation_observers: Default::default(),
-
unique_id: UniqueId::new(),
}
}
@@ -1527,11 +1675,11 @@ impl Node {
// Step 3.
if &*old_doc != document {
// Step 3.1.
- for descendant in node.traverse_preorder() {
+ for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
descendant.set_owner_doc(document);
}
for descendant in node
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::Yes)
.filter_map(|d| d.as_custom_element())
{
// Step 3.2.
@@ -1541,7 +1689,7 @@ impl Node {
None,
);
}
- for descendant in node.traverse_preorder() {
+ for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
// Step 3.3.
vtable_for(&descendant).adopting_steps(&old_doc);
}
@@ -1559,7 +1707,9 @@ impl Node {
) -> ErrorResult {
// Step 1.
match parent.type_id() {
- NodeTypeId::Document(_) | NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => (),
+ NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
+ ()
+ },
_ => return Err(Error::HierarchyRequest),
}
@@ -1587,7 +1737,7 @@ impl Node {
return Err(Error::HierarchyRequest);
}
},
- NodeTypeId::DocumentFragment |
+ NodeTypeId::DocumentFragment(_) |
NodeTypeId::Element(_) |
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) |
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => (),
@@ -1598,7 +1748,7 @@ impl Node {
if parent.is::<Document>() {
match node.type_id() {
// Step 6.1
- NodeTypeId::DocumentFragment => {
+ NodeTypeId::DocumentFragment(_) => {
// Step 6.1.1(b)
if node.children().any(|c| c.is::<Text>()) {
return Err(Error::HierarchyRequest);
@@ -1723,7 +1873,7 @@ impl Node {
}
}
rooted_vec!(let mut new_nodes);
- let new_nodes = if let NodeTypeId::DocumentFragment = node.type_id() {
+ let new_nodes = if let NodeTypeId::DocumentFragment(_) = node.type_id() {
// Step 3.
new_nodes.extend(node.children().map(|kid| Dom::from_ref(&*kid)));
// Step 4.
@@ -1760,7 +1910,7 @@ impl Node {
parent.add_child(*kid, child);
// Step 7.7.
for descendant in kid
- .traverse_preorder()
+ .traverse_preorder(ShadowIncluding::Yes)
.filter_map(DomRoot::downcast::<Element>)
{
// Step 7.7.2.
@@ -1809,7 +1959,7 @@ impl Node {
// Step 3.
rooted_vec!(let mut added_nodes);
let added_nodes = if let Some(node) = node.as_ref() {
- if let NodeTypeId::DocumentFragment = node.type_id() {
+ if let NodeTypeId::DocumentFragment(_) = node.type_id() {
added_nodes.extend(node.children().map(|child| Dom::from_ref(&*child)));
added_nodes.r()
} else {
@@ -1935,7 +2085,7 @@ impl Node {
);
DomRoot::upcast::<Node>(doctype)
},
- NodeTypeId::DocumentFragment => {
+ NodeTypeId::DocumentFragment(_) => {
let doc_fragment = DocumentFragment::new(&document);
DomRoot::upcast::<Node>(doc_fragment)
},
@@ -2068,7 +2218,7 @@ impl Node {
.GetDocumentElement()
.as_ref()
.map_or(ns!(), |elem| elem.locate_namespace(prefix)),
- NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => ns!(),
+ NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => ns!(),
_ => node
.GetParentElement()
.as_ref()
@@ -2093,7 +2243,7 @@ impl NodeMethods for Node {
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => NodeConstants::COMMENT_NODE,
NodeTypeId::Document(_) => NodeConstants::DOCUMENT_NODE,
NodeTypeId::DocumentType => NodeConstants::DOCUMENT_TYPE_NODE,
- NodeTypeId::DocumentFragment => NodeConstants::DOCUMENT_FRAGMENT_NODE,
+ NodeTypeId::DocumentFragment(_) => NodeConstants::DOCUMENT_FRAGMENT_NODE,
NodeTypeId::Element(_) => NodeConstants::ELEMENT_NODE,
}
}
@@ -2113,7 +2263,7 @@ impl NodeMethods for Node {
},
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => DOMString::from("#comment"),
NodeTypeId::DocumentType => self.downcast::<DocumentType>().unwrap().name().clone(),
- NodeTypeId::DocumentFragment => DOMString::from("#document-fragment"),
+ NodeTypeId::DocumentFragment(_) => DOMString::from("#document-fragment"),
NodeTypeId::Document(_) => DOMString::from("#document"),
}
}
@@ -2129,14 +2279,29 @@ impl NodeMethods for Node {
NodeTypeId::CharacterData(..) |
NodeTypeId::Element(..) |
NodeTypeId::DocumentType |
- NodeTypeId::DocumentFragment => Some(self.owner_doc()),
+ NodeTypeId::DocumentFragment(_) => Some(self.owner_doc()),
NodeTypeId::Document(_) => None,
}
}
// https://dom.spec.whatwg.org/#dom-node-getrootnode
- fn GetRootNode(&self) -> DomRoot<Node> {
- self.inclusive_ancestors().last().unwrap()
+ fn GetRootNode(&self, options: &GetRootNodeOptions) -> DomRoot<Node> {
+ if let Some(shadow_root) = self.containing_shadow_root() {
+ return if options.composed {
+ // shadow-including root.
+ shadow_root.Host().upcast::<Node>().GetRootNode(options)
+ } else {
+ DomRoot::from_ref(shadow_root.upcast::<Node>())
+ };
+ }
+
+ if self.is_in_doc() {
+ DomRoot::from_ref(self.owner_doc().upcast::<Node>())
+ } else {
+ self.inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap()
+ }
}
// https://dom.spec.whatwg.org/#dom-node-parentnode
@@ -2198,8 +2363,9 @@ impl NodeMethods for Node {
// https://dom.spec.whatwg.org/#dom-node-textcontent
fn GetTextContent(&self) -> Option<DOMString> {
match self.type_id() {
- NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => {
- let content = Node::collect_text_contents(self.traverse_preorder());
+ NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
+ let content =
+ Node::collect_text_contents(self.traverse_preorder(ShadowIncluding::No));
Some(content)
},
NodeTypeId::CharacterData(..) => {
@@ -2214,7 +2380,7 @@ impl NodeMethods for Node {
fn SetTextContent(&self, value: Option<DOMString>) {
let value = value.unwrap_or_default();
match self.type_id() {
- NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => {
+ NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
// Step 1-2.
let node = if value.is_empty() {
None
@@ -2247,7 +2413,9 @@ impl NodeMethods for Node {
fn ReplaceChild(&self, node: &Node, child: &Node) -> Fallible<DomRoot<Node>> {
// Step 1.
match self.type_id() {
- NodeTypeId::Document(_) | NodeTypeId::DocumentFragment | NodeTypeId::Element(..) => (),
+ NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
+ ()
+ },
_ => return Err(Error::HierarchyRequest),
}
@@ -2277,7 +2445,7 @@ impl NodeMethods for Node {
if self.is::<Document>() {
match node.type_id() {
// Step 6.1
- NodeTypeId::DocumentFragment => {
+ NodeTypeId::DocumentFragment(_) => {
// Step 6.1.1(b)
if node.children().any(|c| c.is::<Text>()) {
return Err(Error::HierarchyRequest);
@@ -2350,7 +2518,10 @@ impl NodeMethods for Node {
// Step 12.
rooted_vec!(let mut nodes);
- let nodes = if node.type_id() == NodeTypeId::DocumentFragment {
+ let nodes = if node.type_id() ==
+ NodeTypeId::DocumentFragment(DocumentFragmentTypeId::DocumentFragment) ||
+ node.type_id() == NodeTypeId::DocumentFragment(DocumentFragmentTypeId::ShadowRoot)
+ {
nodes.extend(node.children().map(|node| Dom::from_ref(&*node)));
nodes.r()
} else {
@@ -2418,8 +2589,11 @@ impl NodeMethods for Node {
}
// https://dom.spec.whatwg.org/#dom-node-clonenode
- fn CloneNode(&self, deep: bool) -> DomRoot<Node> {
- Node::clone(
+ fn CloneNode(&self, deep: bool) -> Fallible<DomRoot<Node>> {
+ if deep && self.is::<ShadowRoot>() {
+ return Err(Error::NotSupported);
+ }
+ Ok(Node::clone(
self,
None,
if deep {
@@ -2427,7 +2601,7 @@ impl NodeMethods for Node {
} else {
CloneChildrenFlag::DoNotCloneChildren
},
- )
+ ))
}
// https://dom.spec.whatwg.org/#dom-node-isequalnode
@@ -2532,8 +2706,12 @@ impl NodeMethods for Node {
// FIXME(emilio): This will eventually need to handle attribute nodes.
- let mut self_and_ancestors = self.inclusive_ancestors().collect::<SmallVec<[_; 20]>>();
- let mut other_and_ancestors = other.inclusive_ancestors().collect::<SmallVec<[_; 20]>>();
+ let mut self_and_ancestors = self
+ .inclusive_ancestors(ShadowIncluding::No)
+ .collect::<SmallVec<[_; 20]>>();
+ let mut other_and_ancestors = other
+ .inclusive_ancestors(ShadowIncluding::No)
+ .collect::<SmallVec<[_; 20]>>();
if self_and_ancestors.last() != other_and_ancestors.last() {
let random = as_uintptr(self_and_ancestors.last().unwrap()) <
@@ -2612,7 +2790,7 @@ impl NodeMethods for Node {
.unwrap()
.GetDocumentElement()
.and_then(|element| element.lookup_prefix(namespace)),
- NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => None,
+ NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => None,
_ => self
.GetParentElement()
.and_then(|element| element.lookup_prefix(namespace)),
@@ -2644,6 +2822,23 @@ pub fn document_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> DomR
derived.upcast().owner_doc()
}
+pub fn containing_shadow_root<T: DerivedFrom<Node> + DomObject>(
+ derived: &T,
+) -> Option<DomRoot<ShadowRoot>> {
+ derived.upcast().containing_shadow_root()
+}
+
+#[allow(unrooted_must_root)]
+pub fn stylesheets_owner_from_node<T: DerivedFrom<Node> + DomObject>(
+ derived: &T,
+) -> StyleSheetListOwner {
+ if let Some(shadow_root) = containing_shadow_root(derived) {
+ StyleSheetListOwner::ShadowRoot(Dom::from_ref(&*shadow_root))
+ } else {
+ StyleSheetListOwner::Document(Dom::from_ref(&*document_from_node(derived)))
+ }
+}
+
pub fn window_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> DomRoot<Window> {
let document = document_from_node(derived);
DomRoot::from_ref(document.window())
@@ -2853,6 +3048,14 @@ impl<'a> ChildrenMutation<'a> {
}
}
+/// The context of the binding to tree of a node.
+pub struct BindContext {
+ /// Whether the tree is connected.
+ pub tree_connected: bool,
+ /// Whether the tree is in the document.
+ pub tree_in_doc: bool,
+}
+
/// The context of the unbinding from a tree of a node when one of its
/// inclusive ancestors is removed.
pub struct UnbindContext<'a> {
@@ -2864,7 +3067,9 @@ pub struct UnbindContext<'a> {
prev_sibling: Option<&'a Node>,
/// The next sibling of the inclusive ancestor that was removed.
pub next_sibling: Option<&'a Node>,
- /// Whether the tree is in a document.
+ /// Whether the tree is connected.
+ pub tree_connected: bool,
+ /// Whether the tree is in doc.
pub tree_in_doc: bool,
}
@@ -2881,6 +3086,7 @@ impl<'a> UnbindContext<'a> {
parent: parent,
prev_sibling: prev_sibling,
next_sibling: next_sibling,
+ tree_connected: parent.is_connected(),
tree_in_doc: parent.is_in_doc(),
}
}
@@ -3028,7 +3234,7 @@ where
let elem_node = elem.upcast::<Node>();
let mut head: usize = 0;
- for node in tree_root.traverse_preorder() {
+ for node in tree_root.traverse_preorder(ShadowIncluding::No) {
let head_node = DomRoot::upcast::<Node>(DomRoot::from_ref(&*self[head]));
if head_node == node {
head += 1;
diff --git a/components/script/dom/range.rs b/components/script/dom/range.rs
index 630c4acd245..de17427664e 100644
--- a/components/script/dom/range.rs
+++ b/components/script/dom/range.rs
@@ -23,7 +23,7 @@ use crate::dom::document::Document;
use crate::dom::documentfragment::DocumentFragment;
use crate::dom::element::Element;
use crate::dom::htmlscriptelement::HTMLScriptElement;
-use crate::dom::node::{Node, UnbindContext};
+use crate::dom::node::{Node, ShadowIncluding, UnbindContext};
use crate::dom::text::Text;
use crate::dom::window::Window;
use dom_struct::dom_struct;
@@ -102,10 +102,10 @@ impl Range {
// https://dom.spec.whatwg.org/#partially-contained
fn partially_contains(&self, node: &Node) -> bool {
self.StartContainer()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.any(|n| &*n == node) !=
self.EndContainer()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.any(|n| &*n == node)
}
@@ -193,8 +193,14 @@ impl Range {
// https://dom.spec.whatwg.org/#dom-range-comparepointnode-offset
fn compare_point(&self, node: &Node, offset: u32) -> Fallible<Ordering> {
let start_node = self.StartContainer();
- let start_node_root = start_node.inclusive_ancestors().last().unwrap();
- let node_root = node.inclusive_ancestors().last().unwrap();
+ let start_node_root = start_node
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
+ let node_root = node
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
if start_node_root != node_root {
// Step 1.
return Err(Error::WrongDocument);
@@ -253,7 +259,10 @@ impl RangeMethods for Range {
fn CommonAncestorContainer(&self) -> DomRoot<Node> {
let end_container = self.EndContainer();
// Step 1.
- for container in self.StartContainer().inclusive_ancestors() {
+ for container in self
+ .StartContainer()
+ .inclusive_ancestors(ShadowIncluding::No)
+ {
// Step 2.
if container.is_inclusive_ancestor_of(&end_container) {
// Step 3.
@@ -368,8 +377,16 @@ impl RangeMethods for Range {
// Step 1.
return Err(Error::NotSupported);
}
- let this_root = self.StartContainer().inclusive_ancestors().last().unwrap();
- let other_root = other.StartContainer().inclusive_ancestors().last().unwrap();
+ let this_root = self
+ .StartContainer()
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
+ let other_root = other
+ .StartContainer()
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
if this_root != other_root {
// Step 2.
return Err(Error::WrongDocument);
@@ -429,8 +446,15 @@ impl RangeMethods for Range {
// https://dom.spec.whatwg.org/#dom-range-intersectsnode
fn IntersectsNode(&self, node: &Node) -> bool {
let start_node = self.StartContainer();
- let start_node_root = self.StartContainer().inclusive_ancestors().last().unwrap();
- let node_root = node.inclusive_ancestors().last().unwrap();
+ let start_node_root = self
+ .StartContainer()
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
+ let node_root = node
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
if start_node_root != node_root {
// Step 1.
return false;
@@ -499,7 +523,7 @@ impl RangeMethods for Range {
fragment.upcast::<Node>().AppendChild(&clone)?;
} else {
// Step 14.1.
- let clone = child.CloneNode(false);
+ let clone = child.CloneNode(/* deep */ false)?;
// Step 14.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 14.3.
@@ -520,7 +544,7 @@ impl RangeMethods for Range {
// Step 15.
for child in contained_children {
// Step 15.1.
- let clone = child.CloneNode(true);
+ let clone = child.CloneNode(/* deep */ true)?;
// Step 15.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
}
@@ -536,7 +560,7 @@ impl RangeMethods for Range {
fragment.upcast::<Node>().AppendChild(&clone)?;
} else {
// Step 17.1.
- let clone = child.CloneNode(false);
+ let clone = child.CloneNode(/* deep */ false)?;
// Step 17.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 17.3.
@@ -572,7 +596,7 @@ impl RangeMethods for Range {
if end_node == start_node {
if let Some(end_data) = end_node.downcast::<CharacterData>() {
// Step 4.1.
- let clone = end_node.CloneNode(true);
+ let clone = end_node.CloneNode(/* deep */ true)?;
// Step 4.2.
let text = end_data.SubstringData(start_offset, end_offset - start_offset);
clone
@@ -613,7 +637,7 @@ impl RangeMethods for Range {
if let Some(start_data) = child.downcast::<CharacterData>() {
assert!(child == start_node);
// Step 15.1.
- let clone = start_node.CloneNode(true);
+ let clone = start_node.CloneNode(/* deep */ true)?;
// Step 15.2.
let text = start_data.SubstringData(start_offset, start_node.len() - start_offset);
clone
@@ -630,7 +654,7 @@ impl RangeMethods for Range {
)?;
} else {
// Step 16.1.
- let clone = child.CloneNode(false);
+ let clone = child.CloneNode(/* deep */ false)?;
// Step 16.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 16.3.
@@ -657,7 +681,7 @@ impl RangeMethods for Range {
if let Some(end_data) = child.downcast::<CharacterData>() {
assert!(child == end_node);
// Step 18.1.
- let clone = end_node.CloneNode(true);
+ let clone = end_node.CloneNode(/* deep */ true)?;
// Step 18.2.
let text = end_data.SubstringData(0, end_offset);
clone
@@ -670,7 +694,7 @@ impl RangeMethods for Range {
end_data.ReplaceData(0, end_offset, DOMString::new())?;
} else {
// Step 19.1.
- let clone = child.CloneNode(false);
+ let clone = child.CloneNode(/* deep */ false)?;
// Step 19.2.
fragment.upcast::<Node>().AppendChild(&clone)?;
// Step 19.3.
@@ -763,7 +787,7 @@ impl RangeMethods for Range {
// Step 11
let new_offset = new_offset +
- if node.type_id() == NodeTypeId::DocumentFragment {
+ if let NodeTypeId::DocumentFragment(_) = node.type_id() {
node.len()
} else {
1
@@ -868,9 +892,9 @@ impl RangeMethods for Range {
let end = self.EndContainer();
if start
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.any(|n| !n.is_inclusive_ancestor_of(&end) && !n.is::<Text>()) ||
- end.inclusive_ancestors()
+ end.inclusive_ancestors(ShadowIncluding::No)
.any(|n| !n.is_inclusive_ancestor_of(&start) && !n.is::<Text>())
{
return Err(Error::InvalidState);
@@ -878,7 +902,9 @@ impl RangeMethods for Range {
// Step 2.
match new_parent.type_id() {
- NodeTypeId::Document(_) | NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => {
+ NodeTypeId::Document(_) |
+ NodeTypeId::DocumentType |
+ NodeTypeId::DocumentFragment(_) => {
return Err(Error::InvalidNodeType);
},
_ => (),
@@ -954,7 +980,7 @@ impl RangeMethods for Range {
let node = self.StartContainer();
let owner_doc = node.owner_doc();
let element = match node.type_id() {
- NodeTypeId::Document(_) | NodeTypeId::DocumentFragment => None,
+ NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) => None,
NodeTypeId::Element(_) => Some(DomRoot::downcast::<Element>(node).unwrap()),
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) |
NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => node.GetParentElement(),
@@ -969,7 +995,10 @@ impl RangeMethods for Range {
let fragment_node = element.parse_fragment(fragment)?;
// Step 4.
- for node in fragment_node.upcast::<Node>().traverse_preorder() {
+ for node in fragment_node
+ .upcast::<Node>()
+ .traverse_preorder(ShadowIncluding::No)
+ {
if let Some(script) = node.downcast::<HTMLScriptElement>() {
script.set_already_started(false);
script.set_parser_inserted(false);
@@ -1046,7 +1075,7 @@ fn bp_position(a_node: &Node, a_offset: u32, b_node: &Node, b_offset: u32) -> Op
}
} else if position & NodeConstants::DOCUMENT_POSITION_CONTAINS != 0 {
// Step 3-1, 3-2.
- let mut b_ancestors = b_node.inclusive_ancestors();
+ let mut b_ancestors = b_node.inclusive_ancestors(ShadowIncluding::No);
let child = b_ancestors
.find(|child| &*child.GetParentNode().unwrap() == a_node)
.unwrap();
diff --git a/components/script/dom/raredata.rs b/components/script/dom/raredata.rs
new file mode 100644
index 00000000000..2372b0b89ab
--- /dev/null
+++ b/components/script/dom/raredata.rs
@@ -0,0 +1,42 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::root::Dom;
+use crate::dom::customelementregistry::{
+ CustomElementDefinition, CustomElementReaction, CustomElementState,
+};
+use crate::dom::mutationobserver::RegisteredObserver;
+use crate::dom::shadowroot::ShadowRoot;
+use std::rc::Rc;
+
+//XXX(ferjm) Ideally merge NodeRareData and ElementRareData so they share
+// storage.
+
+#[derive(Default, JSTraceable, MallocSizeOf)]
+#[must_root]
+pub struct NodeRareData {
+ /// The shadow root the node belongs to.
+ /// This is None if the node is not in a shadow tree or
+ /// if it is a ShadowRoot.
+ pub containing_shadow_root: Option<Dom<ShadowRoot>>,
+ /// Registered observers for this node.
+ pub mutation_observers: Vec<RegisteredObserver>,
+}
+
+#[derive(Default, JSTraceable, MallocSizeOf)]
+#[must_root]
+pub struct ElementRareData {
+ /// https://dom.spec.whatwg.org/#dom-element-shadowroot
+ /// The ShadowRoot this element is host of.
+ /// XXX This is currently not exposed to web content. Only for
+ /// internal use.
+ pub shadow_root: Option<Dom<ShadowRoot>>,
+ /// <https://html.spec.whatwg.org/multipage/#custom-element-reaction-queue>
+ pub custom_element_reaction_queue: Vec<CustomElementReaction>,
+ /// <https://dom.spec.whatwg.org/#concept-element-custom-element-definition>
+ #[ignore_malloc_size_of = "Rc"]
+ pub custom_element_definition: Option<Rc<CustomElementDefinition>>,
+ /// <https://dom.spec.whatwg.org/#concept-element-custom-element-state>
+ pub custom_element_state: CustomElementState,
+}
diff --git a/components/script/dom/servoparser/html.rs b/components/script/dom/servoparser/html.rs
index 965982d8b51..c9e3a06f34f 100644
--- a/components/script/dom/servoparser/html.rs
+++ b/components/script/dom/servoparser/html.rs
@@ -249,7 +249,7 @@ impl<'a> Serialize for &'a Node {
serializer.write_processing_instruction(&pi.target(), &data)?;
},
- NodeTypeId::DocumentFragment => {},
+ NodeTypeId::DocumentFragment(_) => {},
NodeTypeId::Document(_) => panic!("Can't serialize Document node itself"),
NodeTypeId::Element(_) => panic!("Element shouldn't appear here"),
diff --git a/components/script/dom/servoparser/mod.rs b/components/script/dom/servoparser/mod.rs
index 3c0d321910c..9f6a2214ed0 100644
--- a/components/script/dom/servoparser/mod.rs
+++ b/components/script/dom/servoparser/mod.rs
@@ -27,7 +27,7 @@ use crate::dom::htmlformelement::{FormControlElementHelpers, HTMLFormElement};
use crate::dom::htmlimageelement::HTMLImageElement;
use crate::dom::htmlscriptelement::{HTMLScriptElement, ScriptResult};
use crate::dom::htmltemplateelement::HTMLTemplateElement;
-use crate::dom::node::Node;
+use crate::dom::node::{Node, ShadowIncluding};
use crate::dom::performanceentry::PerformanceEntry;
use crate::dom::performancenavigationtiming::PerformanceNavigationTiming;
use crate::dom::processinginstruction::ProcessingInstruction;
@@ -194,7 +194,7 @@ impl ServoParser {
// Step 11.
let form = context_node
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.find(|element| element.is::<HTMLFormElement>());
let fragment_context = FragmentContext {
diff --git a/components/script/dom/shadowroot.rs b/components/script/dom/shadowroot.rs
new file mode 100644
index 00000000000..ae108f8109c
--- /dev/null
+++ b/components/script/dom/shadowroot.rs
@@ -0,0 +1,268 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use crate::dom::bindings::cell::DomRefCell;
+use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::ShadowRootBinding::ShadowRootMethods;
+use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{self, ShadowRootMode};
+use crate::dom::bindings::inheritance::Castable;
+use crate::dom::bindings::num::Finite;
+use crate::dom::bindings::reflector::reflect_dom_object;
+use crate::dom::bindings::root::{Dom, DomRoot, LayoutDom, MutNullableDom};
+use crate::dom::cssstylesheet::CSSStyleSheet;
+use crate::dom::document::Document;
+use crate::dom::documentfragment::DocumentFragment;
+use crate::dom::documentorshadowroot::{DocumentOrShadowRoot, StyleSheetInDocument};
+use crate::dom::element::Element;
+use crate::dom::node::{Node, NodeDamage, NodeFlags, ShadowIncluding};
+use crate::dom::stylesheetlist::{StyleSheetList, StyleSheetListOwner};
+use crate::dom::window::Window;
+use crate::stylesheet_set::StylesheetSetRef;
+use dom_struct::dom_struct;
+use selectors::context::QuirksMode;
+use servo_arc::Arc;
+use servo_atoms::Atom;
+use style::author_styles::AuthorStyles;
+use style::dom::TElement;
+use style::media_queries::Device;
+use style::shared_lock::SharedRwLockReadGuard;
+use style::stylesheets::Stylesheet;
+
+// https://dom.spec.whatwg.org/#interface-shadowroot
+#[dom_struct]
+pub struct ShadowRoot {
+ document_fragment: DocumentFragment,
+ document_or_shadow_root: DocumentOrShadowRoot,
+ document: Dom<Document>,
+ host: Dom<Element>,
+ /// List of author styles associated with nodes in this shadow tree.
+ author_styles: DomRefCell<AuthorStyles<StyleSheetInDocument>>,
+ stylesheet_list: MutNullableDom<StyleSheetList>,
+ window: Dom<Window>,
+}
+
+impl ShadowRoot {
+ #[allow(unrooted_must_root)]
+ fn new_inherited(host: &Element, document: &Document) -> ShadowRoot {
+ let document_fragment = DocumentFragment::new_inherited(document);
+ let node = document_fragment.upcast::<Node>();
+ node.set_flag(NodeFlags::IS_IN_SHADOW_TREE, true);
+ node.set_flag(
+ NodeFlags::IS_CONNECTED,
+ host.upcast::<Node>().is_connected(),
+ );
+ ShadowRoot {
+ document_fragment,
+ document_or_shadow_root: DocumentOrShadowRoot::new(document.window()),
+ document: Dom::from_ref(document),
+ host: Dom::from_ref(host),
+ author_styles: DomRefCell::new(AuthorStyles::new()),
+ stylesheet_list: MutNullableDom::new(None),
+ window: Dom::from_ref(document.window()),
+ }
+ }
+
+ pub fn new(host: &Element, document: &Document) -> DomRoot<ShadowRoot> {
+ reflect_dom_object(
+ Box::new(ShadowRoot::new_inherited(host, document)),
+ document.window(),
+ ShadowRootBinding::Wrap,
+ )
+ }
+
+ pub fn get_focused_element(&self) -> Option<DomRoot<Element>> {
+ //XXX get retargeted focused element
+ None
+ }
+
+ pub fn stylesheet_count(&self) -> usize {
+ self.author_styles.borrow().stylesheets.len()
+ }
+
+ pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
+ let stylesheets = &self.author_styles.borrow().stylesheets;
+
+ stylesheets
+ .get(index)
+ .and_then(|s| s.owner.upcast::<Node>().get_cssom_stylesheet())
+ }
+
+ /// Add a stylesheet owned by `owner` to the list of shadow root sheets, in the
+ /// correct tree position.
+ #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
+ pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
+ let stylesheets = &mut self.author_styles.borrow_mut().stylesheets;
+ let insertion_point = stylesheets
+ .iter()
+ .find(|sheet_in_shadow| {
+ owner
+ .upcast::<Node>()
+ .is_before(sheet_in_shadow.owner.upcast())
+ })
+ .cloned();
+ DocumentOrShadowRoot::add_stylesheet(
+ owner,
+ StylesheetSetRef::Author(stylesheets),
+ sheet,
+ insertion_point,
+ self.document.style_shared_lock(),
+ );
+ }
+
+ /// Remove a stylesheet owned by `owner` from the list of shadow root sheets.
+ #[allow(unrooted_must_root)] // Owner needs to be rooted already necessarily.
+ pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
+ DocumentOrShadowRoot::remove_stylesheet(
+ owner,
+ s,
+ StylesheetSetRef::Author(&mut self.author_styles.borrow_mut().stylesheets),
+ )
+ }
+
+ pub fn invalidate_stylesheets(&self) {
+ self.document.invalidate_shadow_roots_stylesheets();
+ self.author_styles.borrow_mut().stylesheets.force_dirty();
+ // Mark the host element dirty so a reflow will be performed.
+ self.host
+ .upcast::<Node>()
+ .dirty(NodeDamage::NodeStyleDamaged);
+ }
+
+ /// Remove any existing association between the provided id and any elements
+ /// in this shadow tree.
+ pub fn unregister_named_element(&self, to_unregister: &Element, id: Atom) {
+ self.document_or_shadow_root.unregister_named_element(
+ self.document_fragment.id_map(),
+ to_unregister,
+ &id,
+ );
+ }
+
+ /// Associate an element present in this shadow tree with the provided id.
+ pub fn register_named_element(&self, element: &Element, id: Atom) {
+ let root = self
+ .upcast::<Node>()
+ .inclusive_ancestors(ShadowIncluding::No)
+ .last()
+ .unwrap();
+ self.document_or_shadow_root.register_named_element(
+ self.document_fragment.id_map(),
+ element,
+ &id,
+ root,
+ );
+ }
+}
+
+impl ShadowRootMethods for ShadowRoot {
+ // https://html.spec.whatwg.org/multipage/#dom-document-activeelement
+ fn GetActiveElement(&self) -> Option<DomRoot<Element>> {
+ self.document_or_shadow_root
+ .get_active_element(self.get_focused_element(), None, None)
+ }
+
+ // https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
+ fn ElementFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Option<DomRoot<Element>> {
+ // Return the result of running the retargeting algorithm with context object
+ // and the original result as input.
+ match self.document_or_shadow_root.element_from_point(
+ x,
+ y,
+ None,
+ self.document.has_browsing_context(),
+ ) {
+ Some(e) => {
+ let retargeted_node = self.upcast::<Node>().retarget(e.upcast::<Node>());
+ retargeted_node
+ .downcast::<Element>()
+ .map(|n| DomRoot::from_ref(n))
+ },
+ None => None,
+ }
+ }
+
+ // https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint
+ fn ElementsFromPoint(&self, x: Finite<f64>, y: Finite<f64>) -> Vec<DomRoot<Element>> {
+ // Return the result of running the retargeting algorithm with context object
+ // and the original result as input
+ let mut elements = Vec::new();
+ for e in self
+ .document_or_shadow_root
+ .elements_from_point(x, y, None, self.document.has_browsing_context())
+ .iter()
+ {
+ let retargeted_node = self.upcast::<Node>().retarget(e.upcast::<Node>());
+ if let Some(element) = retargeted_node
+ .downcast::<Element>()
+ .map(|n| DomRoot::from_ref(n))
+ {
+ elements.push(element);
+ }
+ }
+ elements
+ }
+
+ /// https://dom.spec.whatwg.org/#dom-shadowroot-mode
+ fn Mode(&self) -> ShadowRootMode {
+ ShadowRootMode::Closed
+ }
+
+ /// https://dom.spec.whatwg.org/#dom-shadowroot-host
+ fn Host(&self) -> DomRoot<Element> {
+ DomRoot::from_ref(&self.host)
+ }
+
+ // https://drafts.csswg.org/cssom/#dom-document-stylesheets
+ fn StyleSheets(&self) -> DomRoot<StyleSheetList> {
+ self.stylesheet_list.or_init(|| {
+ StyleSheetList::new(
+ &self.window,
+ StyleSheetListOwner::ShadowRoot(Dom::from_ref(self)),
+ )
+ })
+ }
+}
+
+#[allow(unsafe_code)]
+pub trait LayoutShadowRootHelpers {
+ unsafe fn get_host_for_layout(&self) -> LayoutDom<Element>;
+ unsafe fn get_style_data_for_layout<'a, E: TElement>(
+ &self,
+ ) -> &'a AuthorStyles<StyleSheetInDocument>;
+ unsafe fn flush_stylesheets<E: TElement>(
+ &self,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ guard: &SharedRwLockReadGuard,
+ );
+}
+
+impl LayoutShadowRootHelpers for LayoutDom<ShadowRoot> {
+ #[inline]
+ #[allow(unsafe_code)]
+ unsafe fn get_host_for_layout(&self) -> LayoutDom<Element> {
+ (*self.unsafe_get()).host.to_layout()
+ }
+
+ #[inline]
+ #[allow(unsafe_code)]
+ unsafe fn get_style_data_for_layout<'a, E: TElement>(
+ &self,
+ ) -> &'a AuthorStyles<StyleSheetInDocument> {
+ (*self.unsafe_get()).author_styles.borrow_for_layout()
+ }
+
+ #[inline]
+ #[allow(unsafe_code)]
+ unsafe fn flush_stylesheets<E: TElement>(
+ &self,
+ device: &Device,
+ quirks_mode: QuirksMode,
+ guard: &SharedRwLockReadGuard,
+ ) {
+ let mut author_styles = (*self.unsafe_get()).author_styles.borrow_mut_for_layout();
+ if author_styles.stylesheets.dirty() {
+ author_styles.flush::<E>(device, quirks_mode, guard);
+ }
+ }
+}
diff --git a/components/script/dom/stylesheetlist.rs b/components/script/dom/stylesheetlist.rs
index c1c5c0b2d5f..236f1c9cd9a 100644
--- a/components/script/dom/stylesheetlist.rs
+++ b/components/script/dom/stylesheetlist.rs
@@ -6,30 +6,85 @@ use crate::dom::bindings::codegen::Bindings::StyleSheetListBinding;
use crate::dom::bindings::codegen::Bindings::StyleSheetListBinding::StyleSheetListMethods;
use crate::dom::bindings::reflector::{reflect_dom_object, Reflector};
use crate::dom::bindings::root::{Dom, DomRoot};
+use crate::dom::cssstylesheet::CSSStyleSheet;
use crate::dom::document::Document;
+use crate::dom::element::Element;
+use crate::dom::shadowroot::ShadowRoot;
use crate::dom::stylesheet::StyleSheet;
use crate::dom::window::Window;
use dom_struct::dom_struct;
+use servo_arc::Arc;
+use style::stylesheets::Stylesheet;
+
+#[must_root]
+#[derive(JSTraceable, MallocSizeOf)]
+pub enum StyleSheetListOwner {
+ Document(Dom<Document>),
+ ShadowRoot(Dom<ShadowRoot>),
+}
+
+impl StyleSheetListOwner {
+ pub fn stylesheet_count(&self) -> usize {
+ match *self {
+ StyleSheetListOwner::Document(ref doc) => doc.stylesheet_count(),
+ StyleSheetListOwner::ShadowRoot(ref shadow_root) => shadow_root.stylesheet_count(),
+ }
+ }
+
+ pub fn stylesheet_at(&self, index: usize) -> Option<DomRoot<CSSStyleSheet>> {
+ match *self {
+ StyleSheetListOwner::Document(ref doc) => doc.stylesheet_at(index),
+ StyleSheetListOwner::ShadowRoot(ref shadow_root) => shadow_root.stylesheet_at(index),
+ }
+ }
+
+ pub fn add_stylesheet(&self, owner: &Element, sheet: Arc<Stylesheet>) {
+ match *self {
+ StyleSheetListOwner::Document(ref doc) => doc.add_stylesheet(owner, sheet),
+ StyleSheetListOwner::ShadowRoot(ref shadow_root) => {
+ shadow_root.add_stylesheet(owner, sheet)
+ },
+ }
+ }
+
+ pub fn remove_stylesheet(&self, owner: &Element, s: &Arc<Stylesheet>) {
+ match *self {
+ StyleSheetListOwner::Document(ref doc) => doc.remove_stylesheet(owner, s),
+ StyleSheetListOwner::ShadowRoot(ref shadow_root) => {
+ shadow_root.remove_stylesheet(owner, s)
+ },
+ }
+ }
+
+ pub fn invalidate_stylesheets(&self) {
+ match *self {
+ StyleSheetListOwner::Document(ref doc) => doc.invalidate_stylesheets(),
+ StyleSheetListOwner::ShadowRoot(ref shadow_root) => {
+ shadow_root.invalidate_stylesheets()
+ },
+ }
+ }
+}
#[dom_struct]
pub struct StyleSheetList {
reflector_: Reflector,
- document: Dom<Document>,
+ document_or_shadow_root: StyleSheetListOwner,
}
impl StyleSheetList {
#[allow(unrooted_must_root)]
- fn new_inherited(doc: Dom<Document>) -> StyleSheetList {
+ fn new_inherited(doc_or_sr: StyleSheetListOwner) -> StyleSheetList {
StyleSheetList {
reflector_: Reflector::new(),
- document: doc,
+ document_or_shadow_root: doc_or_sr,
}
}
#[allow(unrooted_must_root)]
- pub fn new(window: &Window, document: Dom<Document>) -> DomRoot<StyleSheetList> {
+ pub fn new(window: &Window, doc_or_sr: StyleSheetListOwner) -> DomRoot<StyleSheetList> {
reflect_dom_object(
- Box::new(StyleSheetList::new_inherited(document)),
+ Box::new(StyleSheetList::new_inherited(doc_or_sr)),
window,
StyleSheetListBinding::Wrap,
)
@@ -39,14 +94,14 @@ impl StyleSheetList {
impl StyleSheetListMethods for StyleSheetList {
// https://drafts.csswg.org/cssom/#dom-stylesheetlist-length
fn Length(&self) -> u32 {
- self.document.stylesheet_count() as u32
+ self.document_or_shadow_root.stylesheet_count() as u32
}
// https://drafts.csswg.org/cssom/#dom-stylesheetlist-item
fn Item(&self, index: u32) -> Option<DomRoot<StyleSheet>> {
// XXXManishearth this doesn't handle the origin clean flag and is a
// cors vulnerability
- self.document
+ self.document_or_shadow_root
.stylesheet_at(index as usize)
.map(DomRoot::upcast)
}
diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs
index 7a41642a201..f3b8b633232 100644
--- a/components/script/dom/virtualmethods.rs
+++ b/components/script/dom/virtualmethods.rs
@@ -51,7 +51,7 @@ use crate::dom::htmltemplateelement::HTMLTemplateElement;
use crate::dom::htmltextareaelement::HTMLTextAreaElement;
use crate::dom::htmltitleelement::HTMLTitleElement;
use crate::dom::htmlvideoelement::HTMLVideoElement;
-use crate::dom::node::{ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
+use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext};
use crate::dom::svgsvgelement::SVGSVGElement;
use html5ever::LocalName;
use style::attr::AttrValue;
@@ -90,15 +90,15 @@ pub trait VirtualMethods {
}
}
- /// Called when a Node is appended to a tree, where 'tree_in_doc' indicates
+ /// Called when a Node is appended to a tree, where 'tree_connected' indicates
/// whether the tree is part of a Document.
- fn bind_to_tree(&self, tree_in_doc: bool) {
+ fn bind_to_tree(&self, context: &BindContext) {
if let Some(ref s) = self.super_type() {
- s.bind_to_tree(tree_in_doc);
+ s.bind_to_tree(context);
}
}
- /// Called when a Node is removed from a tree, where 'tree_in_doc'
+ /// Called when a Node is removed from a tree, where 'tree_connected'
/// indicates whether the tree is part of a Document.
/// Implements removing steps:
/// <https://dom.spec.whatwg.org/#concept-node-remove-ext>
diff --git a/components/script/dom/webidls/Document.webidl b/components/script/dom/webidls/Document.webidl
index ccc3d339377..85c3362f42e 100644
--- a/components/script/dom/webidls/Document.webidl
+++ b/components/script/dom/webidls/Document.webidl
@@ -132,7 +132,6 @@ partial /*sealed*/ interface Document {
// user interaction
readonly attribute Window?/*Proxy?*/ defaultView;
- readonly attribute Element? activeElement;
boolean hasFocus();
// [CEReactions]
// attribute DOMString designMode;
@@ -199,17 +198,6 @@ partial interface Document {
TouchList createTouchList(Touch... touches);
};
-// https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint
-partial interface Document {
- Element? elementFromPoint(double x, double y);
- sequence<Element> elementsFromPoint(double x, double y);
-};
-
-// https://drafts.csswg.org/cssom/#extensions-to-the-document-interface
-partial interface Document {
- [SameObject] readonly attribute StyleSheetList styleSheets;
-};
-
// https://fullscreen.spec.whatwg.org/#api
partial interface Document {
[LenientSetter] readonly attribute boolean fullscreenEnabled;
@@ -221,3 +209,5 @@ partial interface Document {
attribute EventHandler onfullscreenchange;
attribute EventHandler onfullscreenerror;
};
+
+Document implements DocumentOrShadowRoot;
diff --git a/components/script/dom/webidls/DocumentOrShadowRoot.webidl b/components/script/dom/webidls/DocumentOrShadowRoot.webidl
new file mode 100644
index 00000000000..b5a1a1d0cee
--- /dev/null
+++ b/components/script/dom/webidls/DocumentOrShadowRoot.webidl
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is
+ * https://dom.spec.whatwg.org/#documentorshadowroot
+ * https://w3c.github.io/webcomponents/spec/shadow/#extensions-to-the-documentorshadowroot-mixin
+ */
+
+[NoInterfaceObject]
+interface DocumentOrShadowRoot {
+ // Selection? getSelection();
+ Element? elementFromPoint (double x, double y);
+ sequence<Element> elementsFromPoint (double x, double y);
+ // CaretPosition? caretPositionFromPoint (double x, double y);
+ readonly attribute Element? activeElement;
+ readonly attribute StyleSheetList styleSheets;
+};
diff --git a/components/script/dom/webidls/Element.webidl b/components/script/dom/webidls/Element.webidl
index b7de03ac344..af14f81b373 100644
--- a/components/script/dom/webidls/Element.webidl
+++ b/components/script/dom/webidls/Element.webidl
@@ -81,6 +81,8 @@ interface Element : Node {
void insertAdjacentText(DOMString where_, DOMString data);
[CEReactions, Throws]
void insertAdjacentHTML(DOMString position, DOMString html);
+
+ [Throws, Pref="dom.shadowdom.enabled"] ShadowRoot attachShadow();
};
// http://dev.w3.org/csswg/cssom-view/#extensions-to-the-element-interface
diff --git a/components/script/dom/webidls/Node.webidl b/components/script/dom/webidls/Node.webidl
index cddb776e416..9433e96efa2 100644
--- a/components/script/dom/webidls/Node.webidl
+++ b/components/script/dom/webidls/Node.webidl
@@ -32,7 +32,7 @@ interface Node : EventTarget {
readonly attribute Document? ownerDocument;
[Pure]
- Node getRootNode();
+ Node getRootNode(optional GetRootNodeOptions options);
[Pure]
readonly attribute Node? parentNode;
@@ -58,7 +58,7 @@ interface Node : EventTarget {
[CEReactions]
void normalize();
- [CEReactions]
+ [CEReactions, Throws]
Node cloneNode(optional boolean deep = false);
[Pure]
boolean isEqualNode(Node? node);
@@ -92,3 +92,7 @@ interface Node : EventTarget {
[CEReactions, Throws]
Node removeChild(Node child);
};
+
+dictionary GetRootNodeOptions {
+ boolean composed = false;
+};
diff --git a/components/script/dom/webidls/ShadowRoot.webidl b/components/script/dom/webidls/ShadowRoot.webidl
new file mode 100644
index 00000000000..711d4f853bd
--- /dev/null
+++ b/components/script/dom/webidls/ShadowRoot.webidl
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+/*
+ * The origin of this IDL file is:
+ * https://dom.spec.whatwg.org/#interface-shadowroot
+ */
+
+[Exposed=Window]
+interface ShadowRoot : DocumentFragment {
+ readonly attribute ShadowRootMode mode;
+ readonly attribute Element host;
+};
+
+enum ShadowRootMode { "open", "closed"};
+
+ShadowRoot implements DocumentOrShadowRoot;
diff --git a/components/script/lib.rs b/components/script/lib.rs
index a057540ae22..327209d71db 100644
--- a/components/script/lib.rs
+++ b/components/script/lib.rs
@@ -69,6 +69,7 @@ pub mod script_thread;
mod serviceworker_manager;
mod serviceworkerjob;
mod stylesheet_loader;
+mod stylesheet_set;
mod task_manager;
mod task_queue;
mod task_source;
@@ -84,7 +85,9 @@ mod webdriver_handlers;
///
/// TODO(emilio): A few of the FooHelpers can go away, presumably...
pub mod layout_exports {
- pub use crate::dom::bindings::inheritance::{CharacterDataTypeId, ElementTypeId};
+ pub use crate::dom::bindings::inheritance::{
+ CharacterDataTypeId, DocumentFragmentTypeId, ElementTypeId,
+ };
pub use crate::dom::bindings::inheritance::{HTMLElementTypeId, NodeTypeId, TextTypeId};
pub use crate::dom::bindings::root::LayoutDom;
pub use crate::dom::characterdata::LayoutCharacterDataHelpers;
@@ -92,6 +95,7 @@ pub mod layout_exports {
pub use crate::dom::element::{Element, LayoutElementHelpers, RawLayoutElementHelpers};
pub use crate::dom::node::NodeFlags;
pub use crate::dom::node::{LayoutNodeHelpers, Node};
+ pub use crate::dom::shadowroot::{LayoutShadowRootHelpers, ShadowRoot};
pub use crate::dom::text::Text;
}
diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs
index 84a4182ae3e..4fbaec2b465 100644
--- a/components/script/script_thread.rs
+++ b/components/script/script_thread.rs
@@ -51,7 +51,9 @@ use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlanchorelement::HTMLAnchorElement;
use crate::dom::htmliframeelement::{HTMLIFrameElement, NavigationType};
use crate::dom::mutationobserver::MutationObserver;
-use crate::dom::node::{from_untrusted_node_address, window_from_node, Node, NodeDamage};
+use crate::dom::node::{
+ from_untrusted_node_address, window_from_node, Node, NodeDamage, ShadowIncluding,
+};
use crate::dom::performanceentry::PerformanceEntry;
use crate::dom::performancepainttiming::PerformancePaintTiming;
use crate::dom::serviceworker::TrustedServiceWorkerAddress;
@@ -3097,7 +3099,7 @@ impl ScriptThread {
if let Some(target) = self.topmost_mouse_over_target.get() {
if let Some(anchor) = target
.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLAnchorElement>)
.next()
{
@@ -3121,7 +3123,7 @@ impl ScriptThread {
if let Some(target) = prev_mouse_over_target {
if let Some(_) = target
.upcast::<Node>()
- .inclusive_ancestors()
+ .inclusive_ancestors(ShadowIncluding::No)
.filter_map(DomRoot::downcast::<HTMLAnchorElement>)
.next()
{
diff --git a/components/script/stylesheet_loader.rs b/components/script/stylesheet_loader.rs
index 61c89b35f80..69975c7803b 100644
--- a/components/script/stylesheet_loader.rs
+++ b/components/script/stylesheet_loader.rs
@@ -13,8 +13,9 @@ use crate::dom::eventtarget::EventTarget;
use crate::dom::globalscope::GlobalScope;
use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmllinkelement::{HTMLLinkElement, RequestGenerationId};
-use crate::dom::node::{document_from_node, window_from_node};
+use crate::dom::node::{containing_shadow_root, document_from_node, window_from_node};
use crate::dom::performanceresourcetiming::InitiatorType;
+use crate::dom::shadowroot::ShadowRoot;
use crate::network_listener::{self, NetworkListener, PreInvoke, ResourceTimingListener};
use cssparser::SourceLocation;
use encoding_rs::UTF_8;
@@ -81,6 +82,7 @@ pub struct StylesheetContext {
data: Vec<u8>,
/// The node document for elem when the load was initiated.
document: Trusted<Document>,
+ shadow_root: Option<Trusted<ShadowRoot>>,
origin_clean: bool,
/// A token which must match the generation id of the `HTMLLinkElement` for it to load the stylesheet.
/// This is ignored for `HTMLStyleElement` and imports.
@@ -187,7 +189,11 @@ impl FetchResponseListener for StylesheetContext {
},
}
- document.invalidate_stylesheets();
+ if let Some(ref shadow_root) = self.shadow_root {
+ shadow_root.root().invalidate_stylesheets();
+ } else {
+ document.invalidate_stylesheets();
+ }
// FIXME: Revisit once consensus is reached at:
// https://github.com/whatwg/html/issues/1142
@@ -264,6 +270,7 @@ impl<'a> StylesheetLoader<'a> {
integrity_metadata: String,
) {
let document = document_from_node(self.elem);
+ let shadow_root = containing_shadow_root(self.elem).map(|sr| Trusted::new(&*sr));
let gen = self
.elem
.downcast::<HTMLLinkElement>()
@@ -275,6 +282,7 @@ impl<'a> StylesheetLoader<'a> {
metadata: None,
data: vec![],
document: Trusted::new(&*document),
+ shadow_root,
origin_clean: true,
request_generation_id: gen,
resource_timing: ResourceFetchTiming::new(ResourceTimingType::Resource),
diff --git a/components/script/stylesheet_set.rs b/components/script/stylesheet_set.rs
new file mode 100644
index 00000000000..ddb2f608e57
--- /dev/null
+++ b/components/script/stylesheet_set.rs
@@ -0,0 +1,70 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
+
+use style::media_queries::Device;
+use style::shared_lock::SharedRwLockReadGuard;
+use style::stylesheet_set::{AuthorStylesheetSet, DocumentStylesheetSet};
+use style::stylesheets::StylesheetInDocument;
+
+/// Functionality common to DocumentStylesheetSet and AuthorStylesheetSet.
+pub enum StylesheetSetRef<'a, S>
+where
+ S: StylesheetInDocument + PartialEq + 'static,
+{
+ /// Author stylesheet set.
+ Author(&'a mut AuthorStylesheetSet<S>),
+ /// Document stylesheet set.
+ Document(&'a mut DocumentStylesheetSet<S>),
+}
+
+impl<'a, S> StylesheetSetRef<'a, S>
+where
+ S: StylesheetInDocument + PartialEq + 'static,
+{
+ /// Appends a new stylesheet to the current set.
+ ///
+ /// No device implies not computing invalidations.
+ pub fn append_stylesheet(
+ &mut self,
+ device: Option<&Device>,
+ sheet: S,
+ guard: &SharedRwLockReadGuard,
+ ) {
+ match self {
+ StylesheetSetRef::Author(set) => set.append_stylesheet(device, sheet, guard),
+ StylesheetSetRef::Document(set) => set.append_stylesheet(device, sheet, guard),
+ }
+ }
+
+ /// Insert a given stylesheet before another stylesheet in the document.
+ pub fn insert_stylesheet_before(
+ &mut self,
+ device: Option<&Device>,
+ sheet: S,
+ before_sheet: S,
+ guard: &SharedRwLockReadGuard,
+ ) {
+ match self {
+ StylesheetSetRef::Author(set) => {
+ set.insert_stylesheet_before(device, sheet, before_sheet, guard)
+ },
+ StylesheetSetRef::Document(set) => {
+ set.insert_stylesheet_before(device, sheet, before_sheet, guard)
+ },
+ }
+ }
+
+ /// Remove a given stylesheet from the set.
+ pub fn remove_stylesheet(
+ &mut self,
+ device: Option<&Device>,
+ sheet: S,
+ guard: &SharedRwLockReadGuard,
+ ) {
+ match self {
+ StylesheetSetRef::Author(set) => set.remove_stylesheet(device, sheet, guard),
+ StylesheetSetRef::Document(set) => set.remove_stylesheet(device, sheet, guard),
+ }
+ }
+}
diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs
index a3c143a4faa..ee373126513 100644
--- a/components/script/webdriver_handlers.rs
+++ b/components/script/webdriver_handlers.rs
@@ -22,7 +22,7 @@ use crate::dom::htmlelement::HTMLElement;
use crate::dom::htmliframeelement::HTMLIFrameElement;
use crate::dom::htmlinputelement::HTMLInputElement;
use crate::dom::htmloptionelement::HTMLOptionElement;
-use crate::dom::node::{window_from_node, Node};
+use crate::dom::node::{window_from_node, Node, ShadowIncluding};
use crate::script_thread::Documents;
use cookie::Cookie;
use euclid::{Point2D, Rect, Size2D};
@@ -50,7 +50,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)
})
}
diff --git a/components/script_layout_interface/wrapper_traits.rs b/components/script_layout_interface/wrapper_traits.rs
index 0a7e080bebb..0e1df2ac4c8 100644
--- a/components/script_layout_interface/wrapper_traits.rs
+++ b/components/script_layout_interface/wrapper_traits.rs
@@ -104,6 +104,9 @@ pub trait LayoutNode: Debug + GetLayoutData + TNode {
fn traverse_preorder(self) -> TreeIterator<Self> {
TreeIterator::new(self)
}
+
+ /// Returns whether the node is connected.
+ fn is_connected(&self) -> bool;
}
pub struct ReverseChildrenIterator<ConcreteNode>
diff --git a/components/style/author_styles.rs b/components/style/author_styles.rs
index 37eb68ff4f7..eff64ed6c1e 100644
--- a/components/style/author_styles.rs
+++ b/components/style/author_styles.rs
@@ -18,6 +18,7 @@ use crate::stylist::CascadeData;
/// A set of author stylesheets and their computed representation, such as the
/// ones used for ShadowRoot and XBL.
+#[derive(MallocSizeOf)]
pub struct AuthorStyles<S>
where
S: StylesheetInDocument + PartialEq + 'static,
diff --git a/components/style/stylesheet_set.rs b/components/style/stylesheet_set.rs
index 409978c3a9e..d2c3ad0f88e 100644
--- a/components/style/stylesheet_set.rs
+++ b/components/style/stylesheet_set.rs
@@ -585,6 +585,16 @@ where
self.collection.len() == 0
}
+ /// Returns the `index`th stylesheet in the collection of author styles if present.
+ pub fn get(&self, index: usize) -> Option<&S> {
+ self.collection.get(index)
+ }
+
+ /// Returns the number of author stylesheets.
+ pub fn len(&self) -> usize {
+ self.collection.len()
+ }
+
fn collection_for(
&mut self,
_sheet: &S,
diff --git a/resources/prefs.json b/resources/prefs.json
index 1ca4197be44..b882ad9ca46 100644
--- a/resources/prefs.json
+++ b/resources/prefs.json
@@ -19,6 +19,7 @@
"dom.serviceworker.enabled": false,
"dom.serviceworker.timeout_seconds": 60,
"dom.servoparser.async_html_tokenizer.enabled": false,
+ "dom.shadowdom.enabled": false,
"dom.svg.enabled": false,
"dom.testable_crash.enabled": false,
"dom.testbinding.enabled": false,
diff --git a/tests/unit/script/size_of.rs b/tests/unit/script/size_of.rs
index 4d56b2ab10b..1c5bcace945 100644
--- a/tests/unit/script/size_of.rs
+++ b/tests/unit/script/size_of.rs
@@ -30,10 +30,10 @@ macro_rules! sizeof_checker (
// Update the sizes here
sizeof_checker!(size_event_target, EventTarget, 56);
-sizeof_checker!(size_node, Node, 200);
-sizeof_checker!(size_element, Element, 448);
-sizeof_checker!(size_htmlelement, HTMLElement, 464);
-sizeof_checker!(size_div, HTMLDivElement, 464);
-sizeof_checker!(size_span, HTMLSpanElement, 464);
-sizeof_checker!(size_text, Text, 232);
-sizeof_checker!(size_characterdata, CharacterData, 232);
+sizeof_checker!(size_node, Node, 184);
+sizeof_checker!(size_element, Element, 392);
+sizeof_checker!(size_htmlelement, HTMLElement, 408);
+sizeof_checker!(size_div, HTMLDivElement, 408);
+sizeof_checker!(size_span, HTMLSpanElement, 408);
+sizeof_checker!(size_text, Text, 216);
+sizeof_checker!(size_characterdata, CharacterData, 216);
diff --git a/tests/wpt/metadata/css/cssom/interfaces.html.ini b/tests/wpt/metadata/css/cssom/interfaces.html.ini
index d6a28f8765b..8ff4227d3c3 100644
--- a/tests/wpt/metadata/css/cssom/interfaces.html.ini
+++ b/tests/wpt/metadata/css/cssom/interfaces.html.ini
@@ -1272,6 +1272,3 @@
[CSSStyleDeclaration interface: sheet.cssRules[2\].cssRules[0\].style must inherit property "cssFloat" with the proper type]
expected: FAIL
- [ShadowRoot interface: attribute styleSheets]
- expected: FAIL
-
diff --git a/tests/wpt/metadata/dom/interfaces.html.ini b/tests/wpt/metadata/dom/interfaces.html.ini
index 3fb70edaaca..5586cd6a5b9 100644
--- a/tests/wpt/metadata/dom/interfaces.html.ini
+++ b/tests/wpt/metadata/dom/interfaces.html.ini
@@ -998,30 +998,6 @@
[Unscopable handled correctly for append([object Object\],[object Object\]) on DocumentFragment]
expected: FAIL
- [ShadowRoot interface: existence and properties of interface object]
- expected: FAIL
-
- [ShadowRoot interface object length]
- expected: FAIL
-
- [ShadowRoot interface object name]
- expected: FAIL
-
- [ShadowRoot interface: existence and properties of interface prototype object]
- expected: FAIL
-
- [ShadowRoot interface: existence and properties of interface prototype object's "constructor" property]
- expected: FAIL
-
- [ShadowRoot interface: existence and properties of interface prototype object's @@unscopables property]
- expected: FAIL
-
- [ShadowRoot interface: attribute mode]
- expected: FAIL
-
- [ShadowRoot interface: attribute host]
- expected: FAIL
-
[Element interface: existence and properties of interface prototype object's @@unscopables property]
expected: FAIL
diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json
index a29a6bb5c5a..662740e9f0b 100644
--- a/tests/wpt/mozilla/meta/MANIFEST.json
+++ b/tests/wpt/mozilla/meta/MANIFEST.json
@@ -7301,6 +7301,18 @@
{}
]
],
+ "mozilla/partial_shadow_dom_layout_style.html": [
+ [
+ "mozilla/partial_shadow_dom_layout_style.html",
+ [
+ [
+ "/_mozilla/mozilla/partial_shadow_dom_layout_style_ref.html",
+ "=="
+ ]
+ ],
+ {}
+ ]
+ ],
"mozilla/remove_link_styles.html": [
[
"mozilla/remove_link_styles.html",
@@ -10844,6 +10856,11 @@
{}
]
],
+ "mozilla/partial_shadow_dom_layout_style_ref.html": [
+ [
+ {}
+ ]
+ ],
"mozilla/poster.png": [
[
{}
@@ -13515,6 +13532,12 @@
{}
]
],
+ "mozilla/partial_shadow_dom.html": [
+ [
+ "mozilla/partial_shadow_dom.html",
+ {}
+ ]
+ ],
"mozilla/postmessage_closed.html": [
[
"mozilla/postmessage_closed.html",
@@ -20283,7 +20306,7 @@
"testharness"
],
"mozilla/interfaces.html": [
- "a2b9fd23948319fabc0b5fff550b9565704b6678",
+ "c7e1929c626007e2ef3cdf9f2276620aa995c5b7",
"testharness"
],
"mozilla/interfaces.js": [
@@ -20430,6 +20453,18 @@
"5aff666995fe6cd1d4e84e63a9f6019d04387f8e",
"testharness"
],
+ "mozilla/partial_shadow_dom.html": [
+ "74e308f94036a6dbf5c4223cd3d229f49ffceb4e",
+ "testharness"
+ ],
+ "mozilla/partial_shadow_dom_layout_style.html": [
+ "822e86bc07103351a5ceb2203fd9b6bee0c6367d",
+ "reftest"
+ ],
+ "mozilla/partial_shadow_dom_layout_style_ref.html": [
+ "bf40d2cc35b6b2c1e32afffa0651cb1b26e41fe8",
+ "support"
+ ],
"mozilla/poster.png": [
"33834c3ef095fa9c0080017e1b65b2eb8413eac4",
"support"
diff --git a/tests/wpt/mozilla/meta/mozilla/partial_shadow_dom.html.ini b/tests/wpt/mozilla/meta/mozilla/partial_shadow_dom.html.ini
new file mode 100644
index 00000000000..6b14e5081c1
--- /dev/null
+++ b/tests/wpt/mozilla/meta/mozilla/partial_shadow_dom.html.ini
@@ -0,0 +1,2 @@
+[partial_shadow_dom.html]
+ prefs: [dom.shadowdom.enabled:true]
diff --git a/tests/wpt/mozilla/meta/mozilla/partial_shadow_dom_layout_style.html.ini b/tests/wpt/mozilla/meta/mozilla/partial_shadow_dom_layout_style.html.ini
new file mode 100644
index 00000000000..6612e2fb467
--- /dev/null
+++ b/tests/wpt/mozilla/meta/mozilla/partial_shadow_dom_layout_style.html.ini
@@ -0,0 +1,2 @@
+[partial_shadow_dom_layout_style.html]
+ prefs: [dom.shadowdom.enabled:true]
diff --git a/tests/wpt/mozilla/tests/mozilla/interfaces.html b/tests/wpt/mozilla/tests/mozilla/interfaces.html
index a2b9fd23948..c7e1929c626 100644
--- a/tests/wpt/mozilla/tests/mozilla/interfaces.html
+++ b/tests/wpt/mozilla/tests/mozilla/interfaces.html
@@ -202,6 +202,7 @@ test_interfaces([
"Request",
"Response",
"Screen",
+ "ShadowRoot",
"Storage",
"StorageEvent",
"StyleSheet",
diff --git a/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom.html b/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom.html
new file mode 100644
index 00000000000..74e308f9403
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom.html
@@ -0,0 +1,41 @@
+<!doctype html>
+<head>
+ <meta charset="utf-8">
+ <title></title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <h1 id="header" class="header">Not in the shadows</h1>
+ <div id="host">
+ </div>
+ <script>
+ test(function() {
+ // Attach shadow.
+ var host = document.getElementById('host');
+ var shadowRoot = host.attachShadow();
+ assert_not_equals(shadowRoot, null);
+ assert_equals(shadowRoot.host, host);
+ assert_equals(shadowRoot.mode, 'closed');
+
+ assert_equals(document.body.getElementsByTagName('h1').length, 1);
+ assert_equals(document.body.getElementsByClassName('header').length, 1);
+ assert_equals(document.getElementById('header').textContent, "Not in the shadows");
+ assert_equals(document.querySelectorAll('h1').length, 1);
+ assert_equals(document.body.childNodes.length, 6);
+
+ // Append child to shadow tree and check that its content is encapsulated.
+ var shadowChild = document.createElement('h1');
+ shadowChild.classList.add('header');
+ shadowChild.setAttribute('id', 'header');
+ shadowChild.textContent = 'In the shadows';
+ shadowRoot.appendChild(shadowChild);
+ assert_equals(document.body.getElementsByTagName('h1').length, 1);
+ assert_equals(document.body.getElementsByClassName('header').length, 1);
+ assert_equals(document.querySelectorAll('h1').length, 1);
+ assert_equals(document.body.childNodes.length, 6);
+ assert_equals(shadowRoot.querySelectorAll('h1').length, 1);
+ assert_equals(shadowRoot.getElementById('header').textContent, "In the shadows");
+ });
+ </script>
+</body>
diff --git a/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style.html b/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style.html
new file mode 100644
index 00000000000..822e86bc071
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<html>
+ <meta charset="utf-8">
+ <title>Partial Shadow DOM support - layout & style</title>
+ <link rel="match" href="partial_shadow_dom_layout_style_ref.html">
+ <style>
+ h1 { color: red; }
+ </style>
+ <h1>Not in the shadows</h1>
+ <div id="host">
+ </div>
+ <script>
+ var host = document.getElementById('host');
+ var shadowRoot = host.attachShadow({ mode: 'closed' });
+ var shadowChild = document.createElement('h1');
+ shadowChild.textContent = 'In the shadows';
+ shadowRoot.appendChild(shadowChild);
+ var shadowStyles = document.createElement('style');
+ shadowStyles.textContent = 'h1 { color: blue; }';
+ shadowRoot.appendChild(shadowStyles);
+ </script>
+</html>
diff --git a/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style_ref.html b/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style_ref.html
new file mode 100644
index 00000000000..bf40d2cc35b
--- /dev/null
+++ b/tests/wpt/mozilla/tests/mozilla/partial_shadow_dom_layout_style_ref.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Partial Shadow DOM support - layout & style</title>
+<style>
+ h1 { color: red; }
+ .blue { color: blue; }
+</style>
+<h1>Not in the shadows</h1>
+<div id="host">
+ <h1 class='blue'>In the shadows</h1>
+</div>