aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/element.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/element.rs')
-rw-r--r--components/script/dom/element.rs261
1 files changed, 200 insertions, 61 deletions
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