aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/node.rs
diff options
context:
space:
mode:
Diffstat (limited to 'components/script/dom/node.rs')
-rw-r--r--components/script/dom/node.rs2707
1 files changed, 1793 insertions, 914 deletions
diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs
index 0384c4ed19d..62f340fb215 100644
--- a/components/script/dom/node.rs
+++ b/components/script/dom/node.rs
@@ -1,89 +1,104 @@
/* 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 http://mozilla.org/MPL/2.0/. */
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//! The core DOM types. Defines the basic DOM hierarchy as well as all the HTML elements.
+use crate::document_loader::DocumentLoader;
+use crate::dom::attr::Attr;
+use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut};
+use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
+use crate::dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
+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::{
+ 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};
+use crate::dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId};
+use crate::dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId};
+use crate::dom::bindings::inheritance::{SVGElementTypeId, SVGGraphicsElementTypeId, TextTypeId};
+use crate::dom::bindings::reflector::{reflect_dom_object, DomObject, DomObjectWrap};
+use crate::dom::bindings::root::{Dom, DomRoot, DomSlice, LayoutDom, MutNullableDom};
+use crate::dom::bindings::str::{DOMString, USVString};
+use crate::dom::bindings::xmlname::namespace_from_domstring;
+use crate::dom::characterdata::{CharacterData, LayoutCharacterDataHelpers};
+use crate::dom::cssstylesheet::CSSStyleSheet;
+use crate::dom::customelementregistry::{try_upgrade_element, CallbackReaction};
+use crate::dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
+use crate::dom::documentfragment::DocumentFragment;
+use crate::dom::documenttype::DocumentType;
+use crate::dom::element::{CustomElementCreationMode, Element, ElementCreator};
+use crate::dom::event::{Event, EventBubbles, EventCancelable};
+use crate::dom::eventtarget::EventTarget;
+use crate::dom::htmlbodyelement::HTMLBodyElement;
+use crate::dom::htmlcanvaselement::{HTMLCanvasElement, LayoutHTMLCanvasElementHelpers};
+use crate::dom::htmlcollection::HTMLCollection;
+use crate::dom::htmlelement::HTMLElement;
+use crate::dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
+use crate::dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
+use crate::dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
+use crate::dom::htmllinkelement::HTMLLinkElement;
+use crate::dom::htmlmediaelement::{HTMLMediaElement, LayoutHTMLMediaElementHelpers};
+use crate::dom::htmlmetaelement::HTMLMetaElement;
+use crate::dom::htmlstyleelement::HTMLStyleElement;
+use crate::dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
+use crate::dom::mouseevent::MouseEvent;
+use crate::dom::mutationobserver::{Mutation, MutationObserver, RegisteredObserver};
+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};
+use crate::dom::window::Window;
+use crate::script_thread::ScriptThread;
use app_units::Au;
use devtools_traits::NodeInfo;
-use document_loader::DocumentLoader;
-use dom::bindings::codegen::Bindings::CharacterDataBinding::CharacterDataMethods;
-use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
-use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
-use dom::bindings::codegen::Bindings::HTMLCollectionBinding::HTMLCollectionMethods;
-use dom::bindings::codegen::Bindings::NodeBinding::{NodeConstants, NodeMethods};
-use dom::bindings::codegen::Bindings::NodeListBinding::NodeListMethods;
-use dom::bindings::codegen::Bindings::ProcessingInstructionBinding::ProcessingInstructionMethods;
-use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
-use dom::bindings::codegen::UnionTypes::NodeOrString;
-use dom::bindings::conversions::{self, DerivedFrom};
-use dom::bindings::error::{Error, ErrorResult, Fallible};
-use dom::bindings::inheritance::{Castable, CharacterDataTypeId, ElementTypeId};
-use dom::bindings::inheritance::{EventTargetTypeId, HTMLElementTypeId, NodeTypeId};
-use dom::bindings::inheritance::{SVGElementTypeId, SVGGraphicsElementTypeId};
-use dom::bindings::js::{JS, LayoutJS, MutNullableJS};
-use dom::bindings::js::Root;
-use dom::bindings::js::RootedReference;
-use dom::bindings::reflector::{DomObject, reflect_dom_object};
-use dom::bindings::str::{DOMString, USVString};
-use dom::bindings::xmlname::namespace_from_domstring;
-use dom::characterdata::{CharacterData, LayoutCharacterDataHelpers};
-use dom::cssstylesheet::CSSStyleSheet;
-use dom::document::{Document, DocumentSource, HasBrowsingContext, IsHTMLDocument};
-use dom::documentfragment::DocumentFragment;
-use dom::documenttype::DocumentType;
-use dom::element::{Element, ElementCreator};
-use dom::eventtarget::EventTarget;
-use dom::globalscope::GlobalScope;
-use dom::htmlbodyelement::HTMLBodyElement;
-use dom::htmlcanvaselement::{HTMLCanvasElement, LayoutHTMLCanvasElementHelpers};
-use dom::htmlcollection::HTMLCollection;
-use dom::htmlelement::HTMLElement;
-use dom::htmliframeelement::{HTMLIFrameElement, HTMLIFrameElementLayoutMethods};
-use dom::htmlimageelement::{HTMLImageElement, LayoutHTMLImageElementHelpers};
-use dom::htmlinputelement::{HTMLInputElement, LayoutHTMLInputElementHelpers};
-use dom::htmllinkelement::HTMLLinkElement;
-use dom::htmlmetaelement::HTMLMetaElement;
-use dom::htmlstyleelement::HTMLStyleElement;
-use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
-use dom::nodelist::NodeList;
-use dom::processinginstruction::ProcessingInstruction;
-use dom::range::WeakRangeVec;
-use dom::svgsvgelement::{SVGSVGElement, LayoutSVGSVGElementHelpers};
-use dom::text::Text;
-use dom::virtualmethods::{VirtualMethods, vtable_for};
-use dom::window::Window;
use dom_struct::dom_struct;
-use euclid::point::Point2D;
-use euclid::rect::Rect;
-use euclid::size::Size2D;
-use heapsize::{HeapSizeOf, heap_size_of};
-use html5ever_atoms::{Prefix, Namespace, QualName};
-use js::jsapi::{JSContext, JSObject, JSRuntime};
+use euclid::default::{Point2D, Rect, Size2D, Vector2D};
+use html5ever::{Namespace, Prefix, QualName};
+use js::jsapi::JSObject;
use libc::{self, c_void, uintptr_t};
-use msg::constellation_msg::PipelineId;
-use ref_slice::ref_slice;
-use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData};
-use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress};
-use script_layout_interface::message::Msg;
+use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
+use msg::constellation_msg::{BrowsingContextId, PipelineId};
+use net_traits::image::base::{Image, ImageMetadata};
+use script_layout_interface::message::QueryMsg;
+use script_layout_interface::{HTMLCanvasData, HTMLMediaData, LayoutElementType, LayoutNodeType};
+use script_layout_interface::{SVGSVGData, StyleAndOpaqueLayoutData, TrustedNodeAddress};
use script_traits::DocumentActivity;
use script_traits::UntrustedNodeAddress;
-use selectors::matching::matches_selector_list;
+use selectors::matching::{matches_selector_list, MatchingContext, MatchingMode};
use selectors::parser::SelectorList;
+use servo_arc::Arc;
+use servo_atoms::Atom;
use servo_url::ServoUrl;
-use std::borrow::ToOwned;
+use smallvec::SmallVec;
+use std::borrow::Cow;
use std::cell::{Cell, UnsafeCell};
-use std::cmp::max;
+use std::cmp;
use std::default::Default;
use std::iter;
use std::mem;
use std::ops::Range;
-use std::sync::Arc;
+use std::slice::from_ref;
+use std::sync::Arc as StdArc;
use style::context::QuirksMode;
use style::dom::OpaqueNode;
+use style::properties::ComputedValues;
use style::selector_parser::{SelectorImpl, SelectorParser};
use style::stylesheets::Stylesheet;
-use style::thread_state;
use uuid::Uuid;
//
@@ -97,25 +112,28 @@ pub struct Node {
eventtarget: EventTarget,
/// The parent of this node.
- parent_node: MutNullableJS<Node>,
+ parent_node: MutNullableDom<Node>,
/// The first child of this node.
- first_child: MutNullableJS<Node>,
+ first_child: MutNullableDom<Node>,
/// The last child of this node.
- last_child: MutNullableJS<Node>,
+ last_child: MutNullableDom<Node>,
/// The next sibling of this node.
- next_sibling: MutNullableJS<Node>,
+ next_sibling: MutNullableDom<Node>,
/// The previous sibling of this node.
- prev_sibling: MutNullableJS<Node>,
+ prev_sibling: MutNullableDom<Node>,
/// The document that this node belongs to.
- owner_doc: MutNullableJS<Document>,
+ owner_doc: MutNullableDom<Document>,
+
+ /// Rare node data.
+ rare_data: DomRefCell<Option<Box<NodeRareData>>>,
/// The live list of children return by .childNodes.
- child_list: MutNullableJS<NodeList>,
+ child_list: MutNullableDom<NodeList>,
/// The live count of children of this node.
children_count: Cell<u32>,
@@ -132,41 +150,53 @@ pub struct Node {
/// are this node.
ranges: WeakRangeVec,
- /// Style+Layout information. Only the layout thread may touch this data.
- ///
- /// Must be sent back to the layout thread to be destroyed when this
- /// node is finalized.
- style_and_layout_data: Cell<Option<OpaqueStyleAndLayoutData>>,
-
- unique_id: UniqueId,
+ /// Style+Layout information.
+ #[ignore_malloc_size_of = "trait object"]
+ style_and_layout_data: DomRefCell<Option<Box<StyleAndOpaqueLayoutData>>>,
}
bitflags! {
#[doc = "Flags for node items."]
- #[derive(JSTraceable, HeapSizeOf)]
- pub flags NodeFlags: u8 {
+ #[derive(JSTraceable, MallocSizeOf)]
+ pub struct NodeFlags: u16 {
#[doc = "Specifies whether this node is in a document."]
- const IS_IN_DOC = 0x01,
+ const IS_IN_DOC = 1 << 0;
+
#[doc = "Specifies whether this node needs style recalc on next reflow."]
- const HAS_DIRTY_DESCENDANTS = 0x08,
- // TODO: find a better place to keep this (#4105)
- // https://critic.hoppipolla.co.uk/showcomment?chain=8873
- // Perhaps using a Set in Document?
+ const HAS_DIRTY_DESCENDANTS = 1 << 1;
+
#[doc = "Specifies whether or not there is an authentic click in progress on \
this element."]
- const CLICK_IN_PROGRESS = 0x10,
+ const CLICK_IN_PROGRESS = 1 << 2;
+
#[doc = "Specifies whether this node is focusable and whether it is supposed \
to be reachable with using sequential focus navigation."]
- const SEQUENTIALLY_FOCUSABLE = 0x20,
+ const SEQUENTIALLY_FOCUSABLE = 1 << 3;
- /// Whether any ancestor is a fragmentation container
- const CAN_BE_FRAGMENTED = 0x40,
- #[doc = "Specifies whether this node needs to be dirted when viewport size changed."]
- const DIRTY_ON_VIEWPORT_SIZE_CHANGE = 0x80,
+ // There are two free bits here.
#[doc = "Specifies whether the parser has set an associated form owner for \
this element. Only applicable for form-associatable elements."]
- const PARSER_ASSOCIATED_FORM_OWNER = 0x90,
+ const PARSER_ASSOCIATED_FORM_OWNER = 1 << 6;
+
+ /// Whether this element has a snapshot stored due to a style or
+ /// attribute change.
+ ///
+ /// See the `style::restyle_hints` module.
+ const HAS_SNAPSHOT = 1 << 7;
+
+ /// 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;
+
+ /// Whether this node has a weird parser insertion mode. i.e whether setting innerHTML
+ /// needs extra work or not
+ const HAS_WEIRD_PARSER_INSERTION_MODE = 1 << 11;
}
}
@@ -176,33 +206,16 @@ impl NodeFlags {
}
}
-impl Drop for Node {
- #[allow(unsafe_code)]
- fn drop(&mut self) {
- self.style_and_layout_data.get().map(|d| self.dispose(d));
- }
-}
-
/// suppress observers flag
-/// https://dom.spec.whatwg.org/#concept-node-insert
-/// https://dom.spec.whatwg.org/#concept-node-remove
-#[derive(Copy, Clone, HeapSizeOf)]
+/// <https://dom.spec.whatwg.org/#concept-node-insert>
+/// <https://dom.spec.whatwg.org/#concept-node-remove>
+#[derive(Clone, Copy, MallocSizeOf)]
enum SuppressObserver {
Suppressed,
- Unsuppressed
+ Unsuppressed,
}
impl Node {
- /// Sends the style and layout data, if any, back to the layout thread to be destroyed.
- pub fn dispose(&self, data: OpaqueStyleAndLayoutData) {
- debug_assert!(thread_state::get().is_script());
- let win = window_from_node(self);
- self.style_and_layout_data.set(None);
- if win.layout_chan().send(Msg::ReapStyleAndLayoutData(data)).is_err() {
- warn!("layout thread unreachable - leaking layout data");
- }
- }
-
/// Adds a new child to the end of this node's list of children.
///
/// Fails unless `new_child` is disconnected from the tree.
@@ -212,11 +225,11 @@ impl Node {
assert!(new_child.next_sibling.get().is_none());
match before {
Some(ref before) => {
- assert!(before.parent_node.get().r() == Some(self));
+ assert!(before.parent_node.get().as_deref() == Some(self));
let prev_sibling = before.GetPreviousSibling();
match prev_sibling {
None => {
- assert!(Some(*before) == self.first_child.get().r());
+ assert!(self.first_child.get().as_deref() == Some(*before));
self.first_child.set(Some(new_child));
},
Some(ref prev_sibling) => {
@@ -235,7 +248,7 @@ impl Node {
assert!(last_child.next_sibling.get().is_none());
last_child.next_sibling.set(Some(new_child));
new_child.prev_sibling.set(Some(&last_child));
- }
+ },
}
self.last_child.set(Some(new_child));
@@ -246,66 +259,186 @@ 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() {
- node.set_flag(IS_IN_DOC, parent_in_doc);
+ 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(Some(&*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(HAS_DIRTY_DESCENDANTS));
- vtable_for(&&*node).bind_to_tree(parent_in_doc);
+ debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
+ vtable_for(&&*node).bind_to_tree(&BindContext {
+ tree_connected: parent_is_connected,
+ tree_in_doc: parent_in_doc,
+ });
+ }
+ }
+
+ pub fn clean_up_layout_data(&self) {
+ self.owner_doc().cancel_animations_for_node(self);
+ self.style_and_layout_data.borrow_mut().take();
+ }
+
+ /// Clean up flags and unbind from tree.
+ pub fn complete_remove_subtree(root: &Node, context: &UnbindContext) {
+ for node in root.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 root.traverse_preorder(ShadowIncluding::Yes) {
+ node.clean_up_layout_data();
+
+ // 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>.
+ vtable_for(&&*node).unbind_from_tree(&context);
+ // https://dom.spec.whatwg.org/#concept-node-remove step 14
+ if let Some(element) = node.as_custom_element() {
+ ScriptThread::enqueue_callback_reaction(
+ &*element,
+ CallbackReaction::Disconnected,
+ None,
+ );
+ }
}
- let document = new_child.owner_doc();
- document.content_and_heritage_changed(new_child, NodeDamage::OtherNodeDamage);
}
/// Removes the given child from this node's list of children.
///
/// Fails unless `child` is a child of this node.
fn remove_child(&self, child: &Node, cached_index: Option<u32>) {
- assert!(child.parent_node.get().r() == Some(self));
+ assert!(child.parent_node.get().as_deref() == Some(self));
+ self.note_dirty_descendants();
+
let prev_sibling = child.GetPreviousSibling();
match prev_sibling {
None => {
- self.first_child.set(child.next_sibling.get().r());
- }
+ self.first_child.set(child.next_sibling.get().as_deref());
+ },
Some(ref prev_sibling) => {
- prev_sibling.next_sibling.set(child.next_sibling.get().r());
- }
+ prev_sibling
+ .next_sibling
+ .set(child.next_sibling.get().as_deref());
+ },
}
let next_sibling = child.GetNextSibling();
match next_sibling {
None => {
- self.last_child.set(child.prev_sibling.get().r());
- }
+ self.last_child.set(child.prev_sibling.get().as_deref());
+ },
Some(ref next_sibling) => {
- next_sibling.prev_sibling.set(child.prev_sibling.get().r());
- }
+ next_sibling
+ .prev_sibling
+ .set(child.prev_sibling.get().as_deref());
+ },
}
- let context = UnbindContext::new(self, prev_sibling.r(), cached_index);
+ let context = UnbindContext::new(
+ self,
+ prev_sibling.as_deref(),
+ next_sibling.as_deref(),
+ cached_index,
+ );
child.prev_sibling.set(None);
child.next_sibling.set(None);
child.parent_node.set(None);
self.children_count.set(self.children_count.get() - 1);
- for node in child.traverse_preorder() {
- // Out-of-document elements never have the descendants flag set.
- node.set_flag(IS_IN_DOC | HAS_DIRTY_DESCENDANTS, false);
- }
- for node in child.traverse_preorder() {
- // 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>.
- vtable_for(&&*node).unbind_from_tree(&context);
- node.style_and_layout_data.get().map(|d| node.dispose(d));
- }
-
- self.owner_doc().content_and_heritage_changed(self, NodeDamage::OtherNodeDamage);
- child.owner_doc().content_and_heritage_changed(child, NodeDamage::OtherNodeDamage);
+ Self::complete_remove_subtree(child, &context);
}
pub fn to_untrusted_node_address(&self) -> UntrustedNodeAddress {
UntrustedNodeAddress(self.reflector().get_jsobject().get() as *const c_void)
}
+
+ pub fn to_opaque(&self) -> OpaqueNode {
+ OpaqueNode(self.reflector().get_jsobject().get() as usize)
+ }
+
+ pub fn as_custom_element(&self) -> Option<DomRoot<Element>> {
+ self.downcast::<Element>().and_then(|element| {
+ if element.get_custom_element_definition().is_some() {
+ Some(DomRoot::from_ref(element))
+ } else {
+ None
+ }
+ })
+ }
+
+ // https://html.spec.whatg.org/#fire_a_synthetic_mouse_event
+ pub fn fire_synthetic_mouse_event_not_trusted(&self, name: DOMString) {
+ // Spec says the choice of which global to create
+ // the mouse event on is not well-defined,
+ // and refers to heycam/webidl#135
+ let win = window_from_node(self);
+
+ let mouse_event = MouseEvent::new(
+ &win, // ambiguous in spec
+ name,
+ EventBubbles::Bubbles, // Step 3: bubbles
+ EventCancelable::Cancelable, // Step 3: cancelable,
+ Some(&win), // Step 7: view (this is unambiguous in spec)
+ 0, // detail uninitialized
+ 0, // coordinates uninitialized
+ 0, // coordinates uninitialized
+ 0, // coordinates uninitialized
+ 0, // coordinates uninitialized
+ false,
+ false,
+ false,
+ false, // Step 6 modifier keys TODO compositor hook needed
+ 0, // button uninitialized (and therefore left)
+ 0, // buttons uninitialized (and therefore none)
+ None, // related_target uninitialized,
+ None, // point_in_target uninitialized,
+ );
+
+ // Step 4: TODO composed flag for shadow root
+
+ // Step 5
+ mouse_event.upcast::<Event>().set_trusted(false);
+
+ // Step 8: TODO keyboard modifiers
+
+ mouse_event
+ .upcast::<Event>()
+ .dispatch(self.upcast::<EventTarget>(), false);
+ }
+
+ pub fn parent_directionality(&self) -> String {
+ let mut current = self.GetParentNode();
+
+ loop {
+ match current {
+ Some(node) => {
+ if let Some(directionality) = node
+ .downcast::<HTMLElement>()
+ .and_then(|html_element| html_element.directionality())
+ {
+ return directionality;
+ } else {
+ current = node.GetParentNode();
+ }
+ },
+ None => return "ltr".to_owned(),
+ }
+ }
+ }
}
pub struct QuerySelectorIterator {
@@ -314,8 +447,7 @@ pub struct QuerySelectorIterator {
}
impl<'a> QuerySelectorIterator {
- fn new(iter: TreeIterator, selectors: SelectorList<SelectorImpl>)
- -> QuerySelectorIterator {
+ fn new(iter: TreeIterator, selectors: SelectorList<SelectorImpl>) -> QuerySelectorIterator {
QuerySelectorIterator {
selectors: selectors,
iterator: iter,
@@ -324,30 +456,78 @@ impl<'a> QuerySelectorIterator {
}
impl<'a> Iterator for QuerySelectorIterator {
- type Item = Root<Node>;
-
- fn next(&mut self) -> Option<Root<Node>> {
- let selectors = &self.selectors.0;
- // TODO(cgaebel): Is it worth it to build a bloom filter here
- // (instead of passing `None`)? Probably.
- self.iterator.by_ref().filter_map(|node| {
- if let Some(element) = Root::downcast(node) {
- if matches_selector_list(selectors, &element, None) {
- return Some(Root::upcast(element));
+ type Item = DomRoot<Node>;
+
+ fn next(&mut self) -> Option<DomRoot<Node>> {
+ let selectors = &self.selectors;
+
+ self.iterator
+ .by_ref()
+ .filter_map(|node| {
+ // TODO(cgaebel): Is it worth it to build a bloom filter here
+ // (instead of passing `None`)? Probably.
+ //
+ // FIXME(bholley): Consider an nth-index cache here.
+ let mut ctx = MatchingContext::new(
+ MatchingMode::Normal,
+ None,
+ None,
+ node.owner_doc().quirks_mode(),
+ );
+ if let Some(element) = DomRoot::downcast(node) {
+ if matches_selector_list(selectors, &element, &mut ctx) {
+ return Some(DomRoot::upcast(element));
+ }
}
- }
- None
- }).next()
+ None
+ })
+ .next()
}
}
-
impl Node {
- pub fn teardown(&self) {
- self.style_and_layout_data.get().map(|d| self.dispose(d));
- for kid in self.children() {
- kid.teardown();
+ impl_rare_data!(NodeRareData);
+
+ /// Returns true if this node is before `other` in the same connected DOM
+ /// tree.
+ pub fn is_before(&self, other: &Node) -> bool {
+ let cmp = other.CompareDocumentPosition(self);
+ if cmp & NodeConstants::DOCUMENT_POSITION_DISCONNECTED != 0 {
+ return false;
}
+
+ cmp & NodeConstants::DOCUMENT_POSITION_PRECEDING != 0
+ }
+
+ /// Return all registered mutation observers for this node. Lazily initialize the
+ /// raredata if it does not exist.
+ pub fn registered_mutation_observers_mut(&self) -> RefMut<Vec<RegisteredObserver>> {
+ RefMut::map(self.ensure_rare_data(), |rare_data| {
+ &mut rare_data.mutation_observers
+ })
+ }
+
+ pub fn registered_mutation_observers(&self) -> Option<Ref<Vec<RegisteredObserver>>> {
+ let rare_data: Ref<_> = self.rare_data.borrow();
+
+ if rare_data.is_none() {
+ return None;
+ }
+ Some(Ref::map(rare_data, |rare_data| {
+ &rare_data.as_ref().unwrap().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.ensure_rare_data()
+ .mutation_observers
+ .retain(|reg_obs| &*reg_obs.observer != observer)
}
/// Dumps the subtree rooted at this node, for debugging.
@@ -377,7 +557,25 @@ impl Node {
}
pub fn is_in_doc(&self) -> bool {
- self.flags.get().contains(IS_IN_DOC)
+ 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 has_weird_parser_insertion_mode(&self) -> bool {
+ self.flags
+ .get()
+ .contains(NodeFlags::HAS_WEIRD_PARSER_INSERTION_MODE)
+ }
+
+ pub fn set_weird_parser_insertion_mode(&self) {
+ self.set_flag(NodeFlags::HAS_WEIRD_PARSER_INSERTION_MODE, true)
+ }
+
+ pub fn is_connected(&self) -> bool {
+ self.flags.get().contains(NodeFlags::IS_CONNECTED)
}
/// Returns the type ID of this node.
@@ -392,9 +590,7 @@ impl Node {
pub fn len(&self) -> u32 {
match self.type_id() {
NodeTypeId::DocumentType => 0,
- NodeTypeId::CharacterData(_) => {
- self.downcast::<CharacterData>().unwrap().Length()
- },
+ NodeTypeId::CharacterData(_) => self.downcast::<CharacterData>().unwrap().Length(),
_ => self.children_count(),
}
}
@@ -404,6 +600,11 @@ impl Node {
self.preceding_siblings().count() as u32
}
+ /// Returns true if this node has a parent.
+ pub fn has_parent(&self) -> bool {
+ self.parent_node.get().is_some()
+ }
+
pub fn children_count(&self) -> u32 {
self.children_count.get()
}
@@ -433,8 +634,13 @@ impl Node {
self.flags.set(flags);
}
+ // FIXME(emilio): This and the function below should move to Element.
+ pub fn note_dirty_descendants(&self) {
+ self.owner_doc().note_node_with_dirty_descendants(self);
+ }
+
pub fn has_dirty_descendants(&self) -> bool {
- self.get_flag(HAS_DIRTY_DESCENDANTS)
+ self.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS)
}
pub fn rev_version(&self) {
@@ -442,9 +648,12 @@ impl Node {
// its descendants version, and the document's version. Normally, this will just be
// the document's version, but we do have to deal with the case where the node has moved
// document, so may have a higher version count than its owning document.
- let doc: Root<Node> = Root::upcast(self.owner_doc());
- let version = max(self.inclusive_descendants_version(), doc.inclusive_descendants_version()) + 1;
- for ancestor in self.inclusive_ancestors() {
+ let doc: DomRoot<Node> = DomRoot::upcast(self.owner_doc());
+ let version = cmp::max(
+ self.inclusive_descendants_version(),
+ doc.inclusive_descendants_version(),
+ ) + 1;
+ for ancestor in self.inclusive_ancestors(ShadowIncluding::No) {
ancestor.inclusive_descendants_version.set(version);
}
doc.inclusive_descendants_version.set(version);
@@ -452,15 +661,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) =>
- self.parent_node.get().unwrap().downcast::<Element>().unwrap().restyle(damage),
- NodeTypeId::Element(_) =>
- self.downcast::<Element>().unwrap().restyle(damage),
+ 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),
_ => {},
};
}
@@ -471,22 +686,40 @@ 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) -> NodeSiblingIterator {
- NodeSiblingIterator {
- current: Some(Root::from_ref(self)),
+ pub fn inclusively_following_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
+ current: Some(DomRoot::from_ref(self)),
+ next_node: |n| n.GetNextSibling(),
}
}
- pub fn inclusively_preceding_siblings(&self) -> ReverseSiblingIterator {
- ReverseSiblingIterator {
- current: Some(Root::from_ref(self)),
+ pub fn inclusively_preceding_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
+ current: Some(DomRoot::from_ref(self)),
+ next_node: |n| n.GetPreviousSibling(),
}
}
+ pub fn common_ancestor(
+ &self,
+ other: &Node,
+ shadow_including: ShadowIncluding,
+ ) -> Option<DomRoot<Node>> {
+ for ancestor in self.inclusive_ancestors(shadow_including) {
+ if other
+ .inclusive_ancestors(shadow_including)
+ .any(|node| node == ancestor)
+ {
+ return Some(ancestor);
+ }
+ }
+ None
+ }
+
pub fn is_inclusive_ancestor_of(&self, parent: &Node) -> bool {
self == parent || self.is_ancestor_of(parent)
}
@@ -495,40 +728,51 @@ impl Node {
parent.ancestors().any(|ancestor| &*ancestor == self)
}
- pub fn following_siblings(&self) -> NodeSiblingIterator {
- NodeSiblingIterator {
+ 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(),
+ next_node: |n| n.GetNextSibling(),
}
}
- pub fn preceding_siblings(&self) -> ReverseSiblingIterator {
- ReverseSiblingIterator {
+ pub fn preceding_siblings(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
current: self.GetPreviousSibling(),
+ next_node: |n| n.GetPreviousSibling(),
}
}
pub fn following_nodes(&self, root: &Node) -> FollowingNodeIterator {
FollowingNodeIterator {
- current: Some(Root::from_ref(self)),
- root: Root::from_ref(root),
+ current: Some(DomRoot::from_ref(self)),
+ root: DomRoot::from_ref(root),
}
}
pub fn preceding_nodes(&self, root: &Node) -> PrecedingNodeIterator {
PrecedingNodeIterator {
- current: Some(Root::from_ref(self)),
- root: Root::from_ref(root),
+ current: Some(DomRoot::from_ref(self)),
+ root: DomRoot::from_ref(root),
}
}
- pub fn descending_last_children(&self) -> LastChildIterator {
- LastChildIterator {
+ pub fn descending_last_children(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
current: self.GetLastChild(),
+ next_node: |n| n.GetLastChild(),
}
}
pub fn is_parent_of(&self, child: &Node) -> bool {
- child.parent_node.get().map_or(false, |parent| &*parent == self)
+ child
+ .parent_node
+ .get()
+ .map_or(false, |parent| &*parent == self)
}
pub fn to_trusted_node_address(&self) -> TrustedNodeAddress {
@@ -538,8 +782,7 @@ impl Node {
/// Returns the rendered bounding content box if the element is rendered,
/// and none otherwise.
pub fn bounding_content_box(&self) -> Option<Rect<Au>> {
- window_from_node(self)
- .content_box_query(self.to_trusted_node_address())
+ window_from_node(self).content_box_query(self)
}
pub fn bounding_content_box_or_zero(&self) -> Rect<Au> {
@@ -547,17 +790,15 @@ impl Node {
}
pub fn content_boxes(&self) -> Vec<Rect<Au>> {
- window_from_node(self).content_boxes_query(self.to_trusted_node_address())
+ window_from_node(self).content_boxes_query(self)
}
pub fn client_rect(&self) -> Rect<i32> {
- window_from_node(self).client_rect_query(self.to_trusted_node_address())
+ window_from_node(self).client_rect_query(self)
}
// https://drafts.csswg.org/cssom-view/#dom-element-scrollwidth
// https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
- // https://drafts.csswg.org/cssom-view/#dom-element-scrolltop
- // https://drafts.csswg.org/cssom-view/#dom-element-scrollleft
pub fn scroll_area(&self) -> Rect<i32> {
// Step 1
let document = self.owner_doc();
@@ -566,30 +807,37 @@ impl Node {
let html_element = document.GetDocumentElement();
- let is_body_element = self.downcast::<HTMLBodyElement>()
- .map_or(false, |e| e.is_the_html_body_element());
+ let is_body_element = self
+ .downcast::<HTMLBodyElement>()
+ .map_or(false, |e| e.is_the_html_body_element());
- let scroll_area = window.scroll_area_query(self.to_trusted_node_address());
+ let scroll_area = window.scroll_area_query(self);
- match (document != window.Document(), is_body_element, document.quirks_mode(),
- html_element.r() == self.downcast::<Element>()) {
+ match (
+ document != window.Document(),
+ is_body_element,
+ document.quirks_mode(),
+ html_element.as_deref() == self.downcast::<Element>(),
+ ) {
// Step 2 && Step 5
(true, _, _, _) | (_, false, QuirksMode::Quirks, true) => Rect::zero(),
// Step 6 && Step 7
- (false, false, _, true) | (false, true, QuirksMode::Quirks, _) => {
- Rect::new(Point2D::new(window.ScrollX(), window.ScrollY()),
- Size2D::new(max(window.InnerWidth(), scroll_area.size.width),
- max(window.InnerHeight(), scroll_area.size.height)))
- },
+ (false, false, _, true) | (false, true, QuirksMode::Quirks, _) => Rect::new(
+ Point2D::new(window.ScrollX(), window.ScrollY()),
+ Size2D::new(
+ cmp::max(window.InnerWidth(), scroll_area.size.width),
+ cmp::max(window.InnerHeight(), scroll_area.size.height),
+ ),
+ ),
// Step 9
- _ => scroll_area
+ _ => scroll_area,
}
}
- pub fn scroll_offset(&self) -> Point2D<f32> {
+ pub fn scroll_offset(&self) -> Vector2D<f32> {
let document = self.owner_doc();
let window = document.window();
- window.scroll_offset_query(self)
+ window.scroll_offset_query(self).to_untyped()
}
// https://dom.spec.whatwg.org/#dom-childnode-before
@@ -607,7 +855,7 @@ impl Node {
let viable_previous_sibling = first_node_not_in(self.preceding_siblings(), &nodes);
// Step 4.
- let node = try!(self.owner_doc().node_from_nodes_and_strings(nodes));
+ let node = self.owner_doc().node_from_nodes_and_strings(nodes)?;
// Step 5.
let viable_previous_sibling = match viable_previous_sibling {
@@ -616,7 +864,7 @@ impl Node {
};
// Step 6.
- try!(Node::pre_insert(&node, &parent, viable_previous_sibling.r()));
+ Node::pre_insert(&node, &parent, viable_previous_sibling.as_deref())?;
Ok(())
}
@@ -636,10 +884,10 @@ impl Node {
let viable_next_sibling = first_node_not_in(self.following_siblings(), &nodes);
// Step 4.
- let node = try!(self.owner_doc().node_from_nodes_and_strings(nodes));
+ let node = self.owner_doc().node_from_nodes_and_strings(nodes)?;
// Step 5.
- try!(Node::pre_insert(&node, &parent, viable_next_sibling.r()));
+ Node::pre_insert(&node, &parent, viable_next_sibling.as_deref())?;
Ok(())
}
@@ -656,13 +904,13 @@ impl Node {
// Step 3.
let viable_next_sibling = first_node_not_in(self.following_siblings(), &nodes);
// Step 4.
- let node = try!(self.owner_doc().node_from_nodes_and_strings(nodes));
+ let node = self.owner_doc().node_from_nodes_and_strings(nodes)?;
if self.parent_node == Some(&*parent) {
// Step 5.
- try!(parent.ReplaceChild(&node, self));
+ parent.ReplaceChild(&node, self)?;
} else {
// Step 6.
- try!(Node::pre_insert(&node, &parent, viable_next_sibling.r()));
+ Node::pre_insert(&node, &parent, viable_next_sibling.as_deref())?;
}
Ok(())
}
@@ -671,77 +919,109 @@ impl Node {
pub fn prepend(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
// Step 1.
let doc = self.owner_doc();
- let node = try!(doc.node_from_nodes_and_strings(nodes));
+ let node = doc.node_from_nodes_and_strings(nodes)?;
// Step 2.
let first_child = self.first_child.get();
- Node::pre_insert(&node, self, first_child.r()).map(|_| ())
+ Node::pre_insert(&node, self, first_child.as_deref()).map(|_| ())
}
// https://dom.spec.whatwg.org/#dom-parentnode-append
pub fn append(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
// Step 1.
let doc = self.owner_doc();
- let node = try!(doc.node_from_nodes_and_strings(nodes));
+ let node = doc.node_from_nodes_and_strings(nodes)?;
// Step 2.
self.AppendChild(&node).map(|_| ())
}
+ // https://dom.spec.whatwg.org/#dom-parentnode-replacechildren
+ pub fn replace_children(&self, nodes: Vec<NodeOrString>) -> ErrorResult {
+ // Step 1.
+ let doc = self.owner_doc();
+ let node = doc.node_from_nodes_and_strings(nodes)?;
+ // Step 2.
+ Node::ensure_pre_insertion_validity(&node, self, None)?;
+ // Step 3.
+ Node::replace_all(Some(&node), self);
+ Ok(())
+ }
+
// https://dom.spec.whatwg.org/#dom-parentnode-queryselector
- pub fn query_selector(&self, selectors: DOMString) -> Fallible<Option<Root<Element>>> {
+ pub fn query_selector(&self, selectors: DOMString) -> Fallible<Option<DomRoot<Element>>> {
// Step 1.
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
// Step 2.
- Err(()) => Err(Error::Syntax),
+ Err(_) => Err(Error::Syntax),
// Step 3.
Ok(selectors) => {
- Ok(self.traverse_preorder().filter_map(Root::downcast).find(|element| {
- matches_selector_list(&selectors.0, element, None)
- }))
- }
+ // FIXME(bholley): Consider an nth-index cache here.
+ let mut ctx = MatchingContext::new(
+ MatchingMode::Normal,
+ None,
+ None,
+ self.owner_doc().quirks_mode(),
+ );
+ Ok(self
+ .traverse_preorder(ShadowIncluding::No)
+ .filter_map(DomRoot::downcast)
+ .find(|element| matches_selector_list(&selectors, element, &mut ctx)))
+ },
}
}
- /// https://dom.spec.whatwg.org/#scope-match-a-selectors-string
+ /// <https://dom.spec.whatwg.org/#scope-match-a-selectors-string>
/// Get an iterator over all nodes which match a set of selectors
/// Be careful not to do anything which may manipulate the DOM tree
/// whilst iterating, otherwise the iterator may be invalidated.
- pub fn query_selector_iter(&self, selectors: DOMString)
- -> Fallible<QuerySelectorIterator> {
+ pub fn query_selector_iter(&self, selectors: DOMString) -> Fallible<QuerySelectorIterator> {
// Step 1.
match SelectorParser::parse_author_origin_no_namespace(&selectors) {
// Step 2.
- Err(()) => Err(Error::Syntax),
+ 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))
- }
+ },
}
}
// https://dom.spec.whatwg.org/#dom-parentnode-queryselectorall
#[allow(unsafe_code)]
- pub fn query_selector_all(&self, selectors: DOMString) -> Fallible<Root<NodeList>> {
+ pub fn query_selector_all(&self, selectors: DOMString) -> Fallible<DomRoot<NodeList>> {
let window = window_from_node(self);
- let iter = try!(self.query_selector_iter(selectors));
+ let iter = self.query_selector_iter(selectors)?;
Ok(NodeList::new_simple_list(&window, iter))
}
- pub fn ancestors(&self) -> AncestorIterator {
- AncestorIterator {
- current: self.GetParentNode()
+ pub fn ancestors(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
+ current: self.GetParentNode(),
+ next_node: |n| n.GetParentNode(),
}
}
- pub fn inclusive_ancestors(&self) -> AncestorIterator {
- AncestorIterator {
- current: Some(Root::from_ref(self))
+ /// 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: 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()
+ },
}
}
- pub fn owner_doc(&self) -> Root<Document> {
+ pub fn owner_doc(&self) -> DomRoot<Document> {
self.owner_doc.get().unwrap()
}
@@ -749,28 +1029,44 @@ 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: Option<&ShadowRoot>) {
+ self.ensure_rare_data().containing_shadow_root = shadow_root.map(Dom::from_ref);
+ }
+
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) -> NodeSiblingIterator {
- NodeSiblingIterator {
+ pub fn children(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
current: self.GetFirstChild(),
+ next_node: |n| n.GetNextSibling(),
}
}
- pub fn rev_children(&self) -> ReverseSiblingIterator {
- ReverseSiblingIterator {
+ pub fn rev_children(&self) -> impl Iterator<Item = DomRoot<Node>> {
+ SimpleNodeIterator {
current: self.GetLastChild(),
+ next_node: |n| n.GetPreviousSibling(),
}
}
- pub fn child_elements(&self) -> impl Iterator<Item=Root<Element>> {
- self.children().filter_map(Root::downcast as fn(_) -> _).peekable()
+ pub fn child_elements(&self) -> impl Iterator<Item = DomRoot<Element>> {
+ self.children()
+ .filter_map(DomRoot::downcast as fn(_) -> _)
+ .peekable()
}
pub fn remove_self(&self) {
@@ -780,7 +1076,20 @@ impl Node {
}
pub fn unique_id(&self) -> String {
- self.unique_id.borrow().simple().to_string()
+ let mut rare_data = self.ensure_rare_data();
+
+ if rare_data.unique_id.is_none() {
+ let id = UniqueId::new();
+ ScriptThread::save_node_id(id.borrow().to_simple().to_string());
+ rare_data.unique_id = Some(id);
+ }
+ rare_data
+ .unique_id
+ .as_ref()
+ .unwrap()
+ .borrow()
+ .to_simple()
+ .to_string()
}
pub fn summarize(&self) -> NodeInfo {
@@ -788,7 +1097,9 @@ impl Node {
NodeInfo {
uniqueId: self.unique_id(),
baseURI: base_uri,
- parent: self.GetParentNode().map_or("".to_owned(), |node| node.unique_id()),
+ parent: self
+ .GetParentNode()
+ .map_or("".to_owned(), |node| node.unique_id()),
nodeType: self.NodeType(),
namespaceURI: String::new(), //FIXME
nodeName: String::from(self.NodeName()),
@@ -800,10 +1111,10 @@ impl Node {
systemId: String::new(),
attrs: self.downcast().map(Element::summarize).unwrap_or(vec![]),
- isDocumentElement:
- self.owner_doc()
- .GetDocumentElement()
- .map_or(false, |elem| elem.upcast::<Node>() == self),
+ isDocumentElement: self
+ .owner_doc()
+ .GetDocumentElement()
+ .map_or(false, |elem| elem.upcast::<Node>() == self),
shortValue: self.GetNodeValue().map(String::from).unwrap_or_default(), //FIXME: truncate
incompleteValue: false, //FIXME: reflect truncation
@@ -811,10 +1122,16 @@ impl Node {
}
/// Used by `HTMLTableSectionElement::InsertRow` and `HTMLTableRowElement::InsertCell`
- pub fn insert_cell_or_row<F, G, I>(&self, index: i32, get_items: F, new_child: G) -> Fallible<Root<HTMLElement>>
- where F: Fn() -> Root<HTMLCollection>,
- G: Fn() -> Root<I>,
- I: DerivedFrom<Node> + DerivedFrom<HTMLElement> + DomObject,
+ pub fn insert_cell_or_row<F, G, I>(
+ &self,
+ index: i32,
+ get_items: F,
+ new_child: G,
+ ) -> Fallible<DomRoot<HTMLElement>>
+ where
+ F: Fn() -> DomRoot<HTMLCollection>,
+ G: Fn() -> DomRoot<I>,
+ I: DerivedFrom<Node> + DerivedFrom<HTMLElement> + DomObject,
{
if index < -1 {
return Err(Error::IndexSize);
@@ -822,41 +1139,50 @@ impl Node {
let tr = new_child();
-
{
let tr_node = tr.upcast::<Node>();
if index == -1 {
- try!(self.InsertBefore(tr_node, None));
+ self.InsertBefore(tr_node, None)?;
} else {
let items = get_items();
- let node = match items.elements_iter()
- .map(Root::upcast::<Node>)
- .map(Some)
- .chain(iter::once(None))
- .nth(index as usize) {
+ let node = match items
+ .elements_iter()
+ .map(DomRoot::upcast::<Node>)
+ .map(Some)
+ .chain(iter::once(None))
+ .nth(index as usize)
+ {
None => return Err(Error::IndexSize),
Some(node) => node,
};
- try!(self.InsertBefore(tr_node, node.r()));
+ self.InsertBefore(tr_node, node.as_deref())?;
}
}
- Ok(Root::upcast::<HTMLElement>(tr))
+ Ok(DomRoot::upcast::<HTMLElement>(tr))
}
/// Used by `HTMLTableSectionElement::DeleteRow` and `HTMLTableRowElement::DeleteCell`
- pub fn delete_cell_or_row<F, G>(&self, index: i32, get_items: F, is_delete_type: G) -> ErrorResult
- where F: Fn() -> Root<HTMLCollection>,
- G: Fn(&Element) -> bool
+ pub fn delete_cell_or_row<F, G>(
+ &self,
+ index: i32,
+ get_items: F,
+ is_delete_type: G,
+ ) -> ErrorResult
+ where
+ F: Fn() -> DomRoot<HTMLCollection>,
+ G: Fn(&Element) -> bool,
{
let element = match index {
index if index < -1 => return Err(Error::IndexSize),
-1 => {
let last_child = self.upcast::<Node>().GetLastChild();
- match last_child.and_then(|node| node.inclusively_preceding_siblings()
- .filter_map(Root::downcast::<Element>)
- .filter(|elem| is_delete_type(elem))
- .next()) {
+ match last_child.and_then(|node| {
+ node.inclusively_preceding_siblings()
+ .filter_map(DomRoot::downcast::<Element>)
+ .filter(|elem| is_delete_type(elem))
+ .next()
+ }) {
Some(element) => element,
None => return Ok(()),
}
@@ -883,7 +1209,7 @@ impl Node {
}
}
- pub fn get_cssom_stylesheet(&self) -> Option<Root<CSSStyleSheet>> {
+ pub fn get_cssom_stylesheet(&self) -> Option<DomRoot<CSSStyleSheet>> {
if let Some(node) = self.downcast::<HTMLStyleElement>() {
node.get_cssom_stylesheet()
} else if let Some(node) = self.downcast::<HTMLLinkElement>() {
@@ -894,19 +1220,100 @@ 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>(),
+ );
+ }
+ }
+
+ // https://html.spec.whatwg.org/multipage/#dom-document-nameditem-filter
+ pub fn is_document_named_item(&self, name: &Atom) -> bool {
+ let html_elem_type = match self.type_id() {
+ NodeTypeId::Element(ElementTypeId::HTMLElement(type_)) => type_,
+ _ => return false,
+ };
+ let elem = self
+ .downcast::<Element>()
+ .expect("Node with an Element::HTMLElement NodeTypeID must be an Element");
+ match html_elem_type {
+ HTMLElementTypeId::HTMLFormElement | HTMLElementTypeId::HTMLIFrameElement => {
+ elem.get_name().map_or(false, |n| n == *name)
+ },
+ HTMLElementTypeId::HTMLImageElement =>
+ // Images can match by id, but only when their name is non-empty.
+ {
+ elem.get_name().map_or(false, |n| {
+ n == *name || elem.get_id().map_or(false, |i| i == *name)
+ })
+ },
+ // TODO: Handle <embed> and <object>; these depend on
+ // whether the element is "exposed", a concept which
+ // doesn't fully make sense until embed/object behaviors
+ // are actually implemented.
+ _ => false,
+ }
+ }
+
+ pub fn is_styled(&self) -> bool {
+ self.style_and_layout_data.borrow().is_some()
+ }
+
+ pub fn is_display_none(&self) -> bool {
+ self.style_and_layout_data
+ .borrow()
+ .as_ref()
+ .map_or(true, |data| {
+ data.style_data
+ .element_data
+ .borrow()
+ .styles
+ .primary()
+ .get_box()
+ .display
+ .is_none()
+ })
+ }
+
+ pub fn style(&self) -> Option<Arc<ComputedValues>> {
+ if !window_from_node(self).layout_reflow(QueryMsg::StyleQuery) {
+ return None;
+ }
+ self.style_and_layout_data.borrow().as_ref().map(|data| {
+ data.style_data
+ .element_data
+ .borrow()
+ .styles
+ .primary()
+ .clone()
+ })
+ }
+}
/// Iterate through `nodes` until we find a `Node` that is not in `not_in`
-fn first_node_not_in<I>(mut nodes: I, not_in: &[NodeOrString]) -> Option<Root<Node>>
- where I: Iterator<Item=Root<Node>>
+fn first_node_not_in<I>(mut nodes: I, not_in: &[NodeOrString]) -> Option<DomRoot<Node>>
+where
+ I: Iterator<Item = DomRoot<Node>>,
{
nodes.find(|node| {
- not_in.iter().all(|n| {
- match *n {
- NodeOrString::Node(ref n) => n != node,
- _ => true,
- }
+ not_in.iter().all(|n| match *n {
+ NodeOrString::Node(ref n) => n != node,
+ _ => true,
})
})
}
@@ -914,111 +1321,143 @@ fn first_node_not_in<I>(mut nodes: I, not_in: &[NodeOrString]) -> Option<Root<No
/// If the given untrusted node address represents a valid DOM node in the given runtime,
/// returns it.
#[allow(unsafe_code)]
-pub fn from_untrusted_node_address(_runtime: *mut JSRuntime, candidate: UntrustedNodeAddress)
- -> Root<Node> {
- unsafe {
- // https://github.com/servo/servo/issues/6383
- let candidate: uintptr_t = mem::transmute(candidate.0);
-// let object: *mut JSObject = jsfriendapi::bindgen::JS_GetAddressableObject(runtime,
-// candidate);
- let object: *mut JSObject = mem::transmute(candidate);
- if object.is_null() {
- panic!("Attempted to create a `JS<Node>` from an invalid pointer!")
- }
- let boxed_node = conversions::private_from_object(object) as *const Node;
- Root::from_ref(&*boxed_node)
- }
+pub unsafe fn from_untrusted_node_address(candidate: UntrustedNodeAddress) -> DomRoot<Node> {
+ // https://github.com/servo/servo/issues/6383
+ let candidate: uintptr_t = mem::transmute(candidate.0);
+ // let object: *mut JSObject = jsfriendapi::bindgen::JS_GetAddressableObject(runtime,
+ // candidate);
+ let object: *mut JSObject = mem::transmute(candidate);
+ if object.is_null() {
+ panic!("Attempted to create a `Dom<Node>` from an invalid pointer!")
+ }
+ let boxed_node = conversions::private_from_object(object) as *const Node;
+ DomRoot::from_ref(&*boxed_node)
}
#[allow(unsafe_code)]
-pub trait LayoutNodeHelpers {
- unsafe fn type_id_for_layout(&self) -> NodeTypeId;
-
- unsafe fn parent_node_ref(&self) -> Option<LayoutJS<Node>>;
- unsafe fn first_child_ref(&self) -> Option<LayoutJS<Node>>;
- unsafe fn last_child_ref(&self) -> Option<LayoutJS<Node>>;
- unsafe fn prev_sibling_ref(&self) -> Option<LayoutJS<Node>>;
- unsafe fn next_sibling_ref(&self) -> Option<LayoutJS<Node>>;
-
- unsafe fn owner_doc_for_layout(&self) -> LayoutJS<Document>;
-
- unsafe fn is_element_for_layout(&self) -> bool;
- unsafe fn get_flag(&self, flag: NodeFlags) -> bool;
- unsafe fn set_flag(&self, flag: NodeFlags, value: bool);
-
- unsafe fn children_count(&self) -> u32;
-
- unsafe fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData>;
- unsafe fn init_style_and_layout_data(&self, OpaqueStyleAndLayoutData);
- unsafe fn take_style_and_layout_data(&self) -> OpaqueStyleAndLayoutData;
-
- fn text_content(&self) -> String;
- fn selection(&self) -> Option<Range<usize>>;
- fn image_url(&self) -> Option<ServoUrl>;
- fn canvas_data(&self) -> Option<HTMLCanvasData>;
- fn svg_data(&self) -> Option<SVGSVGData>;
- fn iframe_pipeline_id(&self) -> PipelineId;
- fn opaque(&self) -> OpaqueNode;
+pub trait LayoutNodeHelpers<'dom> {
+ fn type_id_for_layout(self) -> NodeTypeId;
+
+ fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>>;
+ fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>>;
+ fn last_child_ref(self) -> Option<LayoutDom<'dom, Node>>;
+ fn prev_sibling_ref(self) -> Option<LayoutDom<'dom, Node>>;
+ fn next_sibling_ref(self) -> Option<LayoutDom<'dom, Node>>;
+
+ fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document>;
+ fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>>;
+
+ fn is_element_for_layout(self) -> bool;
+ unsafe fn get_flag(self, flag: NodeFlags) -> bool;
+ unsafe fn set_flag(self, flag: NodeFlags, value: bool);
+
+ fn children_count(self) -> u32;
+
+ fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData>;
+ unsafe fn init_style_and_opaque_layout_data(self, data: Box<StyleAndOpaqueLayoutData>);
+ unsafe fn take_style_and_opaque_layout_data(self) -> Box<StyleAndOpaqueLayoutData>;
+
+ fn text_content(self) -> Cow<'dom, str>;
+ fn selection(self) -> Option<Range<usize>>;
+ fn image_url(self) -> Option<ServoUrl>;
+ fn image_density(self) -> Option<f64>;
+ fn image_data(self) -> Option<(Option<StdArc<Image>>, Option<ImageMetadata>)>;
+ fn canvas_data(self) -> Option<HTMLCanvasData>;
+ fn media_data(self) -> Option<HTMLMediaData>;
+ fn svg_data(self) -> Option<SVGSVGData>;
+ fn iframe_browsing_context_id(self) -> Option<BrowsingContextId>;
+ fn iframe_pipeline_id(self) -> Option<PipelineId>;
+ fn opaque(self) -> OpaqueNode;
}
-impl LayoutNodeHelpers for LayoutJS<Node> {
+impl<'dom> LayoutDom<'dom, Node> {
#[inline]
#[allow(unsafe_code)]
- unsafe fn type_id_for_layout(&self) -> NodeTypeId {
- (*self.unsafe_get()).type_id()
+ fn parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
+ unsafe { self.unsafe_get().parent_node.get_inner_as_layout() }
}
+}
+impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
#[inline]
#[allow(unsafe_code)]
- unsafe fn is_element_for_layout(&self) -> bool {
- (*self.unsafe_get()).is::<Element>()
+ fn type_id_for_layout(self) -> NodeTypeId {
+ unsafe { self.unsafe_get().type_id() }
+ }
+
+ #[inline]
+ fn is_element_for_layout(self) -> bool {
+ self.is::<Element>()
+ }
+
+ #[inline]
+ fn composed_parent_node_ref(self) -> Option<LayoutDom<'dom, Node>> {
+ let parent = self.parent_node_ref();
+ if let Some(parent) = parent {
+ if let Some(shadow_root) = parent.downcast::<ShadowRoot>() {
+ return Some(shadow_root.get_host_for_layout().upcast());
+ }
+ }
+ parent
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn parent_node_ref(&self) -> Option<LayoutJS<Node>> {
- (*self.unsafe_get()).parent_node.get_inner_as_layout()
+ fn first_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
+ unsafe { self.unsafe_get().first_child.get_inner_as_layout() }
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn first_child_ref(&self) -> Option<LayoutJS<Node>> {
- (*self.unsafe_get()).first_child.get_inner_as_layout()
+ fn last_child_ref(self) -> Option<LayoutDom<'dom, Node>> {
+ unsafe { self.unsafe_get().last_child.get_inner_as_layout() }
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn last_child_ref(&self) -> Option<LayoutJS<Node>> {
- (*self.unsafe_get()).last_child.get_inner_as_layout()
+ fn prev_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
+ unsafe { self.unsafe_get().prev_sibling.get_inner_as_layout() }
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn prev_sibling_ref(&self) -> Option<LayoutJS<Node>> {
- (*self.unsafe_get()).prev_sibling.get_inner_as_layout()
+ fn next_sibling_ref(self) -> Option<LayoutDom<'dom, Node>> {
+ unsafe { self.unsafe_get().next_sibling.get_inner_as_layout() }
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn next_sibling_ref(&self) -> Option<LayoutJS<Node>> {
- (*self.unsafe_get()).next_sibling.get_inner_as_layout()
+ fn owner_doc_for_layout(self) -> LayoutDom<'dom, Document> {
+ unsafe { self.unsafe_get().owner_doc.get_inner_as_layout().unwrap() }
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn owner_doc_for_layout(&self) -> LayoutJS<Document> {
- (*self.unsafe_get()).owner_doc.get_inner_as_layout().unwrap()
+ fn containing_shadow_root_for_layout(self) -> Option<LayoutDom<'dom, ShadowRoot>> {
+ unsafe {
+ self.unsafe_get()
+ .rare_data
+ .borrow_for_layout()
+ .as_ref()?
+ .containing_shadow_root
+ .as_ref()
+ .map(|sr| sr.to_layout())
+ }
}
+ // FIXME(nox): get_flag/set_flag (especially the latter) are not safe because
+ // they mutate stuff while values of this type can be used from multiple
+ // threads at once, this should be revisited.
+
#[inline]
#[allow(unsafe_code)]
- unsafe fn get_flag(&self, flag: NodeFlags) -> bool {
+ unsafe fn get_flag(self, flag: NodeFlags) -> bool {
(*self.unsafe_get()).flags.get().contains(flag)
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn set_flag(&self, flag: NodeFlags, value: bool) {
+ unsafe fn set_flag(self, flag: NodeFlags, value: bool) {
let this = self.unsafe_get();
let mut flags = (*this).flags.get();
@@ -1033,150 +1472,140 @@ impl LayoutNodeHelpers for LayoutJS<Node> {
#[inline]
#[allow(unsafe_code)]
- unsafe fn children_count(&self) -> u32 {
- (*self.unsafe_get()).children_count.get()
+ fn children_count(self) -> u32 {
+ unsafe { self.unsafe_get().children_count.get() }
}
+ // FIXME(nox): How we handle style and layout data needs to be completely
+ // revisited so we can do that more cleanly and safely in layout 2020.
+
#[inline]
#[allow(unsafe_code)]
- unsafe fn get_style_and_layout_data(&self) -> Option<OpaqueStyleAndLayoutData> {
- (*self.unsafe_get()).style_and_layout_data.get()
+ fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> {
+ unsafe {
+ self.unsafe_get()
+ .style_and_layout_data
+ .borrow_for_layout()
+ .as_deref()
+ }
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn init_style_and_layout_data(&self, val: OpaqueStyleAndLayoutData) {
- debug_assert!((*self.unsafe_get()).style_and_layout_data.get().is_none());
- (*self.unsafe_get()).style_and_layout_data.set(Some(val));
+ unsafe fn init_style_and_opaque_layout_data(self, val: Box<StyleAndOpaqueLayoutData>) {
+ let data = self
+ .unsafe_get()
+ .style_and_layout_data
+ .borrow_mut_for_layout();
+ debug_assert!(data.is_none());
+ *data = Some(val);
}
#[inline]
#[allow(unsafe_code)]
- unsafe fn take_style_and_layout_data(&self) -> OpaqueStyleAndLayoutData {
- let val = (*self.unsafe_get()).style_and_layout_data.get().unwrap();
- (*self.unsafe_get()).style_and_layout_data.set(None);
- val
+ unsafe fn take_style_and_opaque_layout_data(self) -> Box<StyleAndOpaqueLayoutData> {
+ self.unsafe_get()
+ .style_and_layout_data
+ .borrow_mut_for_layout()
+ .take()
+ .unwrap()
}
- #[allow(unsafe_code)]
- fn text_content(&self) -> String {
+ fn text_content(self) -> Cow<'dom, str> {
if let Some(text) = self.downcast::<Text>() {
- return unsafe { text.upcast().data_for_layout().to_owned() };
+ return text.upcast().data_for_layout().into();
}
if let Some(input) = self.downcast::<HTMLInputElement>() {
- return unsafe { input.value_for_layout() };
+ return input.value_for_layout();
}
if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
- return unsafe { area.get_value_for_layout() };
+ return area.value_for_layout().into();
}
panic!("not text!")
}
- #[allow(unsafe_code)]
- fn selection(&self) -> Option<Range<usize>> {
+ fn selection(self) -> Option<Range<usize>> {
if let Some(area) = self.downcast::<HTMLTextAreaElement>() {
- return unsafe { area.selection_for_layout() };
+ return area.selection_for_layout();
}
if let Some(input) = self.downcast::<HTMLInputElement>() {
- return unsafe { input.selection_for_layout() };
+ return input.selection_for_layout();
}
None
}
- #[allow(unsafe_code)]
- fn image_url(&self) -> Option<ServoUrl> {
- unsafe {
- self.downcast::<HTMLImageElement>()
- .expect("not an image!")
- .image_url()
- }
+ fn image_url(self) -> Option<ServoUrl> {
+ self.downcast::<HTMLImageElement>()
+ .expect("not an image!")
+ .image_url()
+ }
+
+ fn image_data(self) -> Option<(Option<StdArc<Image>>, Option<ImageMetadata>)> {
+ self.downcast::<HTMLImageElement>().map(|e| e.image_data())
}
- fn canvas_data(&self) -> Option<HTMLCanvasData> {
+ fn image_density(self) -> Option<f64> {
+ self.downcast::<HTMLImageElement>()
+ .expect("not an image!")
+ .image_density()
+ }
+
+ fn canvas_data(self) -> Option<HTMLCanvasData> {
self.downcast::<HTMLCanvasElement>()
.map(|canvas| canvas.data())
}
- fn svg_data(&self) -> Option<SVGSVGData> {
- self.downcast::<SVGSVGElement>()
- .map(|svg| svg.data())
+ fn media_data(self) -> Option<HTMLMediaData> {
+ self.downcast::<HTMLMediaElement>()
+ .map(|media| media.data())
+ }
+
+ fn svg_data(self) -> Option<SVGSVGData> {
+ self.downcast::<SVGSVGElement>().map(|svg| svg.data())
+ }
+
+ fn iframe_browsing_context_id(self) -> Option<BrowsingContextId> {
+ let iframe_element = self
+ .downcast::<HTMLIFrameElement>()
+ .expect("not an iframe element!");
+ iframe_element.browsing_context_id()
}
- fn iframe_pipeline_id(&self) -> PipelineId {
- let iframe_element = self.downcast::<HTMLIFrameElement>()
+ fn iframe_pipeline_id(self) -> Option<PipelineId> {
+ let iframe_element = self
+ .downcast::<HTMLIFrameElement>()
.expect("not an iframe element!");
- iframe_element.pipeline_id().unwrap()
+ iframe_element.pipeline_id()
}
#[allow(unsafe_code)]
- fn opaque(&self) -> OpaqueNode {
- unsafe {
- OpaqueNode(self.get_jsobject() as usize)
- }
+ fn opaque(self) -> OpaqueNode {
+ unsafe { OpaqueNode(self.get_jsobject() as usize) }
}
}
-
//
// Iteration and traversal
//
-pub struct NodeSiblingIterator {
- current: Option<Root<Node>>,
-}
-
-impl Iterator for NodeSiblingIterator {
- type Item = Root<Node>;
-
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
- self.current = current.GetNextSibling();
- Some(current)
- }
-}
-
-pub struct ReverseSiblingIterator {
- current: Option<Root<Node>>,
-}
-
-impl Iterator for ReverseSiblingIterator {
- type Item = Root<Node>;
-
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
- self.current = current.GetPreviousSibling();
- Some(current)
- }
-}
-
pub struct FollowingNodeIterator {
- current: Option<Root<Node>>,
- root: Root<Node>,
+ current: Option<DomRoot<Node>>,
+ root: DomRoot<Node>,
}
impl FollowingNodeIterator {
/// Skips iterating the children of the current node
- pub fn next_skipping_children(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
-
+ pub fn next_skipping_children(&mut self) -> Option<DomRoot<Node>> {
+ let current = self.current.take()?;
self.next_skipping_children_impl(current)
}
- fn next_skipping_children_impl(&mut self, current: Root<Node>) -> Option<Root<Node>> {
+ fn next_skipping_children_impl(&mut self, current: DomRoot<Node>) -> Option<DomRoot<Node>> {
if self.root == current {
self.current = None;
return None;
@@ -1184,16 +1613,16 @@ impl FollowingNodeIterator {
if let Some(next_sibling) = current.GetNextSibling() {
self.current = Some(next_sibling);
- return current.GetNextSibling()
+ return current.GetNextSibling();
}
- for ancestor in current.inclusive_ancestors() {
+ for ancestor in current.inclusive_ancestors(ShadowIncluding::No) {
if self.root == ancestor {
break;
}
if let Some(next_sibling) = ancestor.GetNextSibling() {
self.current = Some(next_sibling);
- return ancestor.GetNextSibling()
+ return ancestor.GetNextSibling();
}
}
self.current = None;
@@ -1202,18 +1631,15 @@ impl FollowingNodeIterator {
}
impl Iterator for FollowingNodeIterator {
- type Item = Root<Node>;
+ type Item = DomRoot<Node>;
// https://dom.spec.whatwg.org/#concept-tree-following
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
+ fn next(&mut self) -> Option<DomRoot<Node>> {
+ let current = self.current.take()?;
if let Some(first_child) = current.GetFirstChild() {
self.current = Some(first_child);
- return current.GetFirstChild()
+ return current.GetFirstChild();
}
self.next_skipping_children_impl(current)
@@ -1221,19 +1647,16 @@ impl Iterator for FollowingNodeIterator {
}
pub struct PrecedingNodeIterator {
- current: Option<Root<Node>>,
- root: Root<Node>,
+ current: Option<DomRoot<Node>>,
+ root: DomRoot<Node>,
}
impl Iterator for PrecedingNodeIterator {
- type Item = Root<Node>;
+ type Item = DomRoot<Node>;
// https://dom.spec.whatwg.org/#concept-tree-preceding
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
+ fn next(&mut self) -> Option<DomRoot<Node>> {
+ let current = self.current.take()?;
self.current = if self.root == current {
None
@@ -1252,64 +1675,63 @@ impl Iterator for PrecedingNodeIterator {
}
}
-pub struct LastChildIterator {
- current: Option<Root<Node>>,
+struct SimpleNodeIterator<I>
+where
+ I: Fn(&Node) -> Option<DomRoot<Node>>,
+{
+ current: Option<DomRoot<Node>>,
+ next_node: I,
}
-impl Iterator for LastChildIterator {
- type Item = Root<Node>;
+impl<I> Iterator for SimpleNodeIterator<I>
+where
+ I: Fn(&Node) -> Option<DomRoot<Node>>,
+{
+ type Item = DomRoot<Node>;
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
- self.current = current.GetLastChild();
- Some(current)
+ fn next(&mut self) -> Option<Self::Item> {
+ let current = self.current.take();
+ self.current = current.as_ref().and_then(|c| (self.next_node)(c));
+ current
}
}
-pub struct AncestorIterator {
- current: Option<Root<Node>>,
-}
-
-impl Iterator for AncestorIterator {
- type Item = Root<Node>;
-
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
- self.current = current.GetParentNode();
- Some(current)
- }
+/// Whether a tree traversal should pass shadow tree boundaries.
+#[derive(Clone, Copy, PartialEq)]
+pub enum ShadowIncluding {
+ No,
+ Yes,
}
pub struct TreeIterator {
- current: Option<Root<Node>>,
+ 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(Root::from_ref(root)),
+ current: Some(DomRoot::from_ref(root)),
depth: 0,
+ shadow_including: shadow_including == ShadowIncluding::Yes,
}
}
- pub fn next_skipping_children(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
+ pub fn next_skipping_children(&mut self) -> Option<DomRoot<Node>> {
+ let current = self.current.take()?;
self.next_skipping_children_impl(current)
}
- fn next_skipping_children_impl(&mut self, current: Root<Node>) -> Option<Root<Node>> {
- for ancestor in current.inclusive_ancestors() {
+ fn next_skipping_children_impl(&mut self, current: DomRoot<Node>) -> Option<DomRoot<Node>> {
+ let iter = current.inclusive_ancestors(if self.shadow_including {
+ ShadowIncluding::Yes
+ } else {
+ ShadowIncluding::No
+ });
+
+ for ancestor in iter {
if self.depth == 0 {
break;
}
@@ -1319,21 +1741,28 @@ impl TreeIterator {
}
self.depth -= 1;
}
- debug_assert!(self.depth == 0);
+ debug_assert_eq!(self.depth, 0);
self.current = None;
Some(current)
}
}
impl Iterator for TreeIterator {
- type Item = Root<Node>;
+ type Item = DomRoot<Node>;
// https://dom.spec.whatwg.org/#concept-tree-order
- fn next(&mut self) -> Option<Root<Node>> {
- let current = match self.current.take() {
- None => return None,
- Some(current) => current,
- };
+ // 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;
@@ -1345,24 +1774,23 @@ impl Iterator for TreeIterator {
}
/// Specifies whether children must be recursively cloned or not.
-#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
+#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
pub enum CloneChildrenFlag {
CloneChildren,
- DoNotCloneChildren
+ DoNotCloneChildren,
}
-fn as_uintptr<T>(t: &T) -> uintptr_t { t as *const T as uintptr_t }
+fn as_uintptr<T>(t: &T) -> uintptr_t {
+ t as *const T as uintptr_t
+}
impl Node {
- pub fn reflect_node<N>(
- node: Box<N>,
- document: &Document,
- wrap_fn: unsafe extern "Rust" fn(*mut JSContext, &GlobalScope, Box<N>) -> Root<N>)
- -> Root<N>
- where N: DerivedFrom<Node> + DomObject
+ pub fn reflect_node<N>(node: Box<N>, document: &Document) -> DomRoot<N>
+ where
+ N: DerivedFrom<Node> + DomObject + DomObjectWrap,
{
let window = document.window();
- reflect_dom_object(node, window, wrap_fn)
+ reflect_dom_object(node, window)
}
pub fn new_inherited(doc: &Document) -> Node {
@@ -1371,7 +1799,10 @@ impl Node {
#[allow(unrooted_must_root)]
pub fn new_document_node() -> Node {
- Node::new_(NodeFlags::new() | IS_IN_DOC, None)
+ Node::new_(
+ NodeFlags::new() | NodeFlags::IS_IN_DOC | NodeFlags::IS_CONNECTED,
+ None,
+ )
}
#[allow(unrooted_must_root)]
@@ -1384,47 +1815,66 @@ impl Node {
last_child: Default::default(),
next_sibling: Default::default(),
prev_sibling: Default::default(),
- owner_doc: MutNullableJS::new(doc),
+ owner_doc: MutNullableDom::new(doc),
+ rare_data: Default::default(),
child_list: Default::default(),
children_count: Cell::new(0u32),
flags: Cell::new(flags),
inclusive_descendants_version: Cell::new(0),
ranges: WeakRangeVec::new(),
- style_and_layout_data: Cell::new(None),
-
- unique_id: UniqueId::new(),
+ style_and_layout_data: Default::default(),
}
}
// https://dom.spec.whatwg.org/#concept-node-adopt
pub fn adopt(node: &Node, document: &Document) {
+ document.add_script_and_layout_blocker();
+
// Step 1.
let old_doc = node.owner_doc();
+ old_doc.add_script_and_layout_blocker();
// Step 2.
node.remove_self();
+ // Step 3.
if &*old_doc != document {
- // Step 3.
- for descendant in node.traverse_preorder() {
+ // Step 3.1.
+ for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
descendant.set_owner_doc(document);
}
- // Step 4.
- for descendant in node.traverse_preorder() {
+ for descendant in node
+ .traverse_preorder(ShadowIncluding::Yes)
+ .filter_map(|d| d.as_custom_element())
+ {
+ // Step 3.2.
+ ScriptThread::enqueue_callback_reaction(
+ &*descendant,
+ CallbackReaction::Adopted(old_doc.clone(), DomRoot::from_ref(document)),
+ None,
+ );
+ }
+ for descendant in node.traverse_preorder(ShadowIncluding::Yes) {
+ // Step 3.3.
vtable_for(&descendant).adopting_steps(&old_doc);
}
}
+
+ old_doc.remove_script_and_layout_blocker();
+ document.remove_script_and_layout_blocker();
}
// https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
- pub fn ensure_pre_insertion_validity(node: &Node,
- parent: &Node,
- child: Option<&Node>) -> ErrorResult {
+ pub fn ensure_pre_insertion_validity(
+ node: &Node,
+ parent: &Node,
+ child: Option<&Node>,
+ ) -> ErrorResult {
// Step 1.
match parent.type_id() {
- NodeTypeId::Document(_) |
- NodeTypeId::DocumentFragment |
- NodeTypeId::Element(..) => (),
- _ => return Err(Error::HierarchyRequest)
+ NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
+ ()
+ },
+ _ => return Err(Error::HierarchyRequest),
}
// Step 2.
@@ -1441,7 +1891,7 @@ impl Node {
// Step 4-5.
match node.type_id() {
- NodeTypeId::CharacterData(CharacterDataTypeId::Text) => {
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => {
if parent.is::<Document>() {
return Err(Error::HierarchyRequest);
}
@@ -1451,22 +1901,21 @@ impl Node {
return Err(Error::HierarchyRequest);
}
},
- NodeTypeId::DocumentFragment |
+ NodeTypeId::DocumentFragment(_) |
NodeTypeId::Element(_) |
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) |
NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => (),
- NodeTypeId::Document(_) => return Err(Error::HierarchyRequest)
+ NodeTypeId::Document(_) => return Err(Error::HierarchyRequest),
+ NodeTypeId::Attr => unreachable!(),
}
// Step 6.
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>())
- {
+ if node.children().any(|c| c.is::<Text>()) {
return Err(Error::HierarchyRequest);
}
match node.child_elements().count() {
@@ -1477,9 +1926,11 @@ impl Node {
return Err(Error::HierarchyRequest);
}
if let Some(child) = child {
- if child.inclusively_following_siblings()
- .any(|child| child.is_doctype()) {
- return Err(Error::HierarchyRequest);
+ if child
+ .inclusively_following_siblings()
+ .any(|child| child.is_doctype())
+ {
+ return Err(Error::HierarchyRequest);
}
}
},
@@ -1493,24 +1944,25 @@ impl Node {
return Err(Error::HierarchyRequest);
}
if let Some(ref child) = child {
- if child.inclusively_following_siblings()
- .any(|child| child.is_doctype()) {
- return Err(Error::HierarchyRequest);
+ if child
+ .inclusively_following_siblings()
+ .any(|child| child.is_doctype())
+ {
+ return Err(Error::HierarchyRequest);
}
}
},
// Step 6.3
NodeTypeId::DocumentType => {
- if parent.children()
- .any(|c| c.is_doctype())
- {
+ if parent.children().any(|c| c.is_doctype()) {
return Err(Error::HierarchyRequest);
}
match child {
Some(child) => {
- if parent.children()
- .take_while(|c| &**c != child)
- .any(|c| c.is::<Element>())
+ if parent
+ .children()
+ .take_while(|c| &**c != child)
+ .any(|c| c.is::<Element>())
{
return Err(Error::HierarchyRequest);
}
@@ -1524,25 +1976,25 @@ impl Node {
},
NodeTypeId::CharacterData(_) => (),
NodeTypeId::Document(_) => unreachable!(),
+ NodeTypeId::Attr => unreachable!(),
}
}
Ok(())
}
// https://dom.spec.whatwg.org/#concept-node-pre-insert
- pub fn pre_insert(node: &Node, parent: &Node, child: Option<&Node>)
- -> Fallible<Root<Node>> {
+ pub fn pre_insert(node: &Node, parent: &Node, child: Option<&Node>) -> Fallible<DomRoot<Node>> {
// Step 1.
- try!(Node::ensure_pre_insertion_validity(node, parent, child));
+ Node::ensure_pre_insertion_validity(node, parent, child)?;
// Steps 2-3.
let reference_child_root;
let reference_child = match child {
Some(child) if child == node => {
reference_child_root = node.GetNextSibling();
- reference_child_root.r()
+ reference_child_root.as_deref()
},
- _ => child
+ _ => child,
};
// Step 4.
@@ -1550,19 +2002,28 @@ impl Node {
Node::adopt(node, &document);
// Step 5.
- Node::insert(node, parent, reference_child, SuppressObserver::Unsuppressed);
+ Node::insert(
+ node,
+ parent,
+ reference_child,
+ SuppressObserver::Unsuppressed,
+ );
// Step 6.
- Ok(Root::from_ref(node))
+ Ok(DomRoot::from_ref(node))
}
// https://dom.spec.whatwg.org/#concept-node-insert
- fn insert(node: &Node,
- parent: &Node,
- child: Option<&Node>,
- suppress_observers: SuppressObserver) {
+ fn insert(
+ node: &Node,
+ parent: &Node,
+ child: Option<&Node>,
+ suppress_observers: SuppressObserver,
+ ) {
+ node.owner_doc().add_script_and_layout_blocker();
debug_assert!(&*node.owner_doc() == &*parent.owner_doc());
- debug_assert!(child.map_or(true, |child| Some(parent) == child.GetParentNode().r()));
+ debug_assert!(child.map_or(true, |child| Some(parent) ==
+ child.GetParentNode().as_deref()));
// Step 1.
let count = if node.is::<DocumentFragment>() {
@@ -1579,27 +2040,34 @@ 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| JS::from_ref(&*kid)));
- // Step 4: mutation observers.
- // Step 5.
- for kid in new_nodes.r() {
- Node::remove(*kid, node, SuppressObserver::Suppressed);
+ new_nodes.extend(node.children().map(|kid| Dom::from_ref(&*kid)));
+ // Step 4.
+ for kid in &*new_nodes {
+ Node::remove(kid, node, SuppressObserver::Suppressed);
}
+ // Step 5.
vtable_for(&node).children_changed(&ChildrenMutation::replace_all(new_nodes.r(), &[]));
+
+ let mutation = Mutation::ChildList {
+ added: None,
+ removed: Some(new_nodes.r()),
+ prev: None,
+ next: None,
+ };
+ MutationObserver::queue_a_mutation_record(&node, mutation);
+
new_nodes.r()
} else {
// Step 3.
- ref_slice(&node)
+ from_ref(&node)
};
- // Step 6: mutation observers.
+ // Step 6.
let previous_sibling = match suppress_observers {
- SuppressObserver::Unsuppressed => {
- match child {
- Some(child) => child.GetPreviousSibling(),
- None => parent.GetLastChild(),
- }
+ SuppressObserver::Unsuppressed => match child {
+ Some(child) => child.GetPreviousSibling(),
+ None => parent.GetLastChild(),
},
SuppressObserver::Suppressed => None,
};
@@ -1607,16 +2075,48 @@ impl Node {
for kid in new_nodes {
// Step 7.1.
parent.add_child(*kid, child);
- // Step 7.2: insertion steps.
+ // Step 7.7.
+ for descendant in kid
+ .traverse_preorder(ShadowIncluding::Yes)
+ .filter_map(DomRoot::downcast::<Element>)
+ {
+ // Step 7.7.2.
+ if descendant.is_connected() {
+ if descendant.get_custom_element_definition().is_some() {
+ // Step 7.7.2.1.
+ ScriptThread::enqueue_callback_reaction(
+ &*descendant,
+ CallbackReaction::Connected,
+ None,
+ );
+ } else {
+ // Step 7.7.2.2.
+ try_upgrade_element(&*descendant);
+ }
+ }
+ }
}
if let SuppressObserver::Unsuppressed = suppress_observers {
- vtable_for(&parent).children_changed(
- &ChildrenMutation::insert(previous_sibling.r(), new_nodes, child));
+ vtable_for(&parent).children_changed(&ChildrenMutation::insert(
+ previous_sibling.as_deref(),
+ new_nodes,
+ child,
+ ));
+
+ let mutation = Mutation::ChildList {
+ added: Some(new_nodes),
+ removed: None,
+ prev: previous_sibling.as_deref(),
+ next: child,
+ };
+ MutationObserver::queue_a_mutation_record(&parent, mutation);
}
+ node.owner_doc().remove_script_and_layout_blocker();
}
// https://dom.spec.whatwg.org/#concept-node-replace-all
pub fn replace_all(node: Option<&Node>, parent: &Node) {
+ parent.owner_doc().add_script_and_layout_blocker();
// Step 1.
if let Some(node) = node {
Node::adopt(node, &*parent.owner_doc());
@@ -1626,47 +2126,73 @@ 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() {
- added_nodes.extend(node.children().map(|child| JS::from_ref(&*child)));
+ if let NodeTypeId::DocumentFragment(_) = node.type_id() {
+ added_nodes.extend(node.children().map(|child| Dom::from_ref(&*child)));
added_nodes.r()
} else {
- ref_slice(node)
+ from_ref(node)
}
} else {
&[] as &[&Node]
};
// Step 4.
- for child in removed_nodes.r() {
- Node::remove(*child, parent, SuppressObserver::Suppressed);
+ for child in &*removed_nodes {
+ Node::remove(child, parent, SuppressObserver::Suppressed);
}
// Step 5.
if let Some(node) = node {
Node::insert(node, parent, None, SuppressObserver::Suppressed);
}
- // Step 6: mutation observers.
- vtable_for(&parent).children_changed(
- &ChildrenMutation::replace_all(removed_nodes.r(), added_nodes));
+ // Step 6.
+ vtable_for(&parent).children_changed(&ChildrenMutation::replace_all(
+ removed_nodes.r(),
+ added_nodes,
+ ));
+
+ if !removed_nodes.is_empty() || !added_nodes.is_empty() {
+ let mutation = Mutation::ChildList {
+ added: Some(added_nodes),
+ removed: Some(removed_nodes.r()),
+ prev: None,
+ next: None,
+ };
+ MutationObserver::queue_a_mutation_record(&parent, mutation);
+ }
+ parent.owner_doc().remove_script_and_layout_blocker();
+ }
+
+ // https://dom.spec.whatwg.org/multipage/#string-replace-all
+ pub fn string_replace_all(string: DOMString, parent: &Node) {
+ if string.len() == 0 {
+ Node::replace_all(None, parent);
+ } else {
+ let text = Text::new(string, &document_from_node(parent));
+ Node::replace_all(Some(text.upcast::<Node>()), parent);
+ };
}
// https://dom.spec.whatwg.org/#concept-node-pre-remove
- fn pre_remove(child: &Node, parent: &Node) -> Fallible<Root<Node>> {
+ fn pre_remove(child: &Node, parent: &Node) -> Fallible<DomRoot<Node>> {
// Step 1.
match child.GetParentNode() {
Some(ref node) if &**node != parent => return Err(Error::NotFound),
None => return Err(Error::NotFound),
- _ => ()
+ _ => (),
}
// Step 2.
Node::remove(child, parent, SuppressObserver::Unsuppressed);
// Step 3.
- Ok(Root::from_ref(child))
+ Ok(DomRoot::from_ref(child))
}
// https://dom.spec.whatwg.org/#concept-node-remove
fn remove(node: &Node, parent: &Node, suppress_observers: SuppressObserver) {
- assert!(node.GetParentNode().map_or(false, |node_parent| &*node_parent == parent));
+ parent.owner_doc().add_script_and_layout_blocker();
+ assert!(node
+ .GetParentNode()
+ .map_or(false, |node_parent| &*node_parent == parent));
let cached_index = {
if parent.ranges.is_empty() {
None
@@ -1692,36 +2218,66 @@ impl Node {
// Step 11. transient registered observers
// Step 12.
if let SuppressObserver::Unsuppressed = suppress_observers {
- vtable_for(&parent).children_changed(
- &ChildrenMutation::replace(old_previous_sibling.r(),
- &Some(&node), &[],
- old_next_sibling.r()));
+ vtable_for(&parent).children_changed(&ChildrenMutation::replace(
+ old_previous_sibling.as_deref(),
+ &Some(&node),
+ &[],
+ old_next_sibling.as_deref(),
+ ));
+
+ let removed = [node];
+ let mutation = Mutation::ChildList {
+ added: None,
+ removed: Some(&removed),
+ prev: old_previous_sibling.as_deref(),
+ next: old_next_sibling.as_deref(),
+ };
+ MutationObserver::queue_a_mutation_record(&parent, mutation);
}
+ parent.owner_doc().remove_script_and_layout_blocker();
}
// https://dom.spec.whatwg.org/#concept-node-clone
- pub fn clone(node: &Node, maybe_doc: Option<&Document>,
- clone_children: CloneChildrenFlag) -> Root<Node> {
+ pub fn clone(
+ node: &Node,
+ maybe_doc: Option<&Document>,
+ clone_children: CloneChildrenFlag,
+ ) -> DomRoot<Node> {
// Step 1.
let document = match maybe_doc {
- Some(doc) => Root::from_ref(doc),
- None => node.owner_doc()
+ Some(doc) => DomRoot::from_ref(doc),
+ None => node.owner_doc(),
};
// Step 2.
// XXXabinader: clone() for each node as trait?
- let copy: Root<Node> = match node.type_id() {
+ let copy: DomRoot<Node> = match node.type_id() {
NodeTypeId::DocumentType => {
let doctype = node.downcast::<DocumentType>().unwrap();
- let doctype = DocumentType::new(doctype.name().clone(),
- Some(doctype.public_id().clone()),
- Some(doctype.system_id().clone()),
- &document);
- Root::upcast::<Node>(doctype)
+ let doctype = DocumentType::new(
+ doctype.name().clone(),
+ Some(doctype.public_id().clone()),
+ Some(doctype.system_id().clone()),
+ &document,
+ );
+ DomRoot::upcast::<Node>(doctype)
+ },
+ NodeTypeId::Attr => {
+ let attr = node.downcast::<Attr>().unwrap();
+ let attr = Attr::new(
+ &document,
+ attr.local_name().clone(),
+ attr.value().clone(),
+ attr.name().clone(),
+ attr.namespace().clone(),
+ attr.prefix().cloned(),
+ None,
+ );
+ DomRoot::upcast::<Node>(attr)
},
- NodeTypeId::DocumentFragment => {
+ NodeTypeId::DocumentFragment(_) => {
let doc_fragment = DocumentFragment::new(&document);
- Root::upcast::<Node>(doc_fragment)
+ DomRoot::upcast::<Node>(doc_fragment)
},
NodeTypeId::CharacterData(_) => {
let cdata = node.downcast::<CharacterData>().unwrap();
@@ -1736,33 +2292,46 @@ impl Node {
};
let window = document.window();
let loader = DocumentLoader::new(&*document.loader());
- let document = Document::new(window, HasBrowsingContext::No,
- Some(document.url()),
- // https://github.com/whatwg/dom/issues/378
- document.origin().clone(),
- is_html_doc, None,
- None, DocumentActivity::Inactive,
- DocumentSource::NotFromParser, loader,
- None, None);
- Root::upcast::<Node>(document)
+ let document = Document::new(
+ window,
+ HasBrowsingContext::No,
+ Some(document.url()),
+ // https://github.com/whatwg/dom/issues/378
+ document.origin().clone(),
+ is_html_doc,
+ None,
+ None,
+ DocumentActivity::Inactive,
+ DocumentSource::NotFromParser,
+ loader,
+ None,
+ None,
+ Default::default(),
+ );
+ DomRoot::upcast::<Node>(document)
},
NodeTypeId::Element(..) => {
let element = node.downcast::<Element>().unwrap();
let name = QualName {
+ prefix: element.prefix().as_ref().map(|p| Prefix::from(&**p)),
ns: element.namespace().clone(),
- local: element.local_name().clone()
+ local: element.local_name().clone(),
};
- let element = Element::create(name,
- element.prefix().map(|p| Prefix::from(&**p)),
- &document, ElementCreator::ScriptCreated);
- Root::upcast::<Node>(element)
+ let element = Element::create(
+ name,
+ element.get_is(),
+ &document,
+ ElementCreator::ScriptCreated,
+ CustomElementCreationMode::Asynchronous,
+ );
+ DomRoot::upcast::<Node>(element)
},
};
// Step 3.
let document = match copy.downcast::<Document>() {
- Some(doc) => Root::from_ref(doc),
- None => Root::from_ref(&*document),
+ Some(doc) => DomRoot::from_ref(doc),
+ None => DomRoot::from_ref(&*document),
};
assert!(copy.owner_doc() == document);
@@ -1779,14 +2348,16 @@ impl Node {
let copy_elem = copy.downcast::<Element>().unwrap();
for attr in node_elem.attrs().iter() {
- copy_elem.push_new_attribute(attr.local_name().clone(),
- attr.value().clone(),
- attr.name().clone(),
- attr.namespace().clone(),
- attr.prefix().cloned());
+ copy_elem.push_new_attribute(
+ attr.local_name().clone(),
+ attr.value().clone(),
+ attr.name().clone(),
+ attr.namespace().clone(),
+ attr.prefix().cloned(),
+ );
}
},
- _ => ()
+ _ => (),
}
// Step 5: cloning steps.
@@ -1795,8 +2366,7 @@ impl Node {
// Step 6.
if clone_children == CloneChildrenFlag::CloneChildren {
for child in node.children() {
- let child_copy = Node::clone(&child, Some(&document),
- clone_children);
+ let child_copy = Node::clone(&child, Some(&document), clone_children);
let _inserted_node = Node::pre_insert(&child_copy, &copy, None);
}
}
@@ -1805,12 +2375,17 @@ impl Node {
copy
}
- /// https://html.spec.whatwg.org/multipage/#child-text-content
+ /// <https://html.spec.whatwg.org/multipage/#child-text-content>
pub fn child_text_content(&self) -> DOMString {
Node::collect_text_contents(self.children())
}
- pub fn collect_text_contents<T: Iterator<Item=Root<Node>>>(iterator: T) -> DOMString {
+ /// <https://html.spec.whatwg.org/multipage/#descendant-text-content>
+ pub fn descendant_text_content(&self) -> DOMString {
+ Node::collect_text_contents(self.traverse_preorder(ShadowIncluding::No))
+ }
+
+ pub fn collect_text_contents<T: Iterator<Item = DomRoot<Node>>>(iterator: T) -> DOMString {
let mut content = String::new();
for node in iterator {
if let Some(ref text) = node.downcast::<Text>() {
@@ -1824,26 +2399,31 @@ impl Node {
match namespace {
ns!() => None,
// FIXME(ajeffrey): convert directly from Namespace to DOMString
- _ => Some(DOMString::from(&*namespace))
+ _ => Some(DOMString::from(&*namespace)),
}
}
// https://dom.spec.whatwg.org/#locate-a-namespace
pub fn locate_namespace(node: &Node, prefix: Option<DOMString>) -> Namespace {
match node.type_id() {
- NodeTypeId::Element(_) => {
- node.downcast::<Element>().unwrap().locate_namespace(prefix)
- },
- NodeTypeId::Document(_) => {
- node.downcast::<Document>().unwrap()
- .GetDocumentElement().as_ref()
- .map_or(ns!(), |elem| elem.locate_namespace(prefix))
- },
- NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => ns!(),
- _ => {
- node.GetParentElement().as_ref()
- .map_or(ns!(), |elem| elem.locate_namespace(prefix))
- }
+ NodeTypeId::Element(_) => node.downcast::<Element>().unwrap().locate_namespace(prefix),
+ NodeTypeId::Attr => node
+ .downcast::<Attr>()
+ .unwrap()
+ .GetOwnerElement()
+ .as_ref()
+ .map_or(ns!(), |elem| elem.locate_namespace(prefix)),
+ NodeTypeId::Document(_) => node
+ .downcast::<Document>()
+ .unwrap()
+ .GetDocumentElement()
+ .as_ref()
+ .map_or(ns!(), |elem| elem.locate_namespace(prefix)),
+ NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => ns!(),
+ _ => node
+ .GetParentElement()
+ .as_ref()
+ .map_or(ns!(), |elem| elem.locate_namespace(prefix)),
}
}
}
@@ -1852,39 +2432,42 @@ impl NodeMethods for Node {
// https://dom.spec.whatwg.org/#dom-node-nodetype
fn NodeType(&self) -> u16 {
match self.type_id() {
- NodeTypeId::CharacterData(CharacterDataTypeId::Text) =>
- NodeConstants::TEXT_NODE,
- NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) =>
- NodeConstants::PROCESSING_INSTRUCTION_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::Element(_) =>
- NodeConstants::ELEMENT_NODE,
+ NodeTypeId::Attr => NodeConstants::ATTRIBUTE_NODE,
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
+ NodeConstants::TEXT_NODE
+ },
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::CDATASection)) => {
+ NodeConstants::CDATA_SECTION_NODE
+ },
+ NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
+ NodeConstants::PROCESSING_INSTRUCTION_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::Element(_) => NodeConstants::ELEMENT_NODE,
}
}
// https://dom.spec.whatwg.org/#dom-node-nodename
fn NodeName(&self) -> DOMString {
match self.type_id() {
- NodeTypeId::Element(..) => {
- self.downcast::<Element>().unwrap().TagName()
- }
- NodeTypeId::CharacterData(CharacterDataTypeId::Text) => DOMString::from("#text"),
+ NodeTypeId::Attr => self.downcast::<Attr>().unwrap().qualified_name(),
+ NodeTypeId::Element(..) => self.downcast::<Element>().unwrap().TagName(),
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::Text)) => {
+ DOMString::from("#text")
+ },
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(TextTypeId::CDATASection)) => {
+ DOMString::from("#cdata-section")
+ },
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction) => {
self.downcast::<ProcessingInstruction>().unwrap().Target()
- }
- NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => DOMString::from("#comment"),
- NodeTypeId::DocumentType => {
- self.downcast::<DocumentType>().unwrap().name().clone()
},
- NodeTypeId::DocumentFragment => DOMString::from("#document-fragment"),
- NodeTypeId::Document(_) => DOMString::from("#document")
+ NodeTypeId::CharacterData(CharacterDataTypeId::Comment) => DOMString::from("#comment"),
+ NodeTypeId::DocumentType => self.downcast::<DocumentType>().unwrap().name().clone(),
+ NodeTypeId::DocumentFragment(_) => DOMString::from("#document-fragment"),
+ NodeTypeId::Document(_) => DOMString::from("#document"),
}
}
@@ -1893,30 +2476,47 @@ impl NodeMethods for Node {
USVString(String::from(self.owner_doc().base_url().as_str()))
}
+ // https://dom.spec.whatwg.org/#dom-node-isconnected
+ fn IsConnected(&self) -> bool {
+ return self.is_connected();
+ }
+
// https://dom.spec.whatwg.org/#dom-node-ownerdocument
- fn GetOwnerDocument(&self) -> Option<Root<Document>> {
+ fn GetOwnerDocument(&self) -> Option<DomRoot<Document>> {
match self.type_id() {
- NodeTypeId::CharacterData(..) |
- NodeTypeId::Element(..) |
- NodeTypeId::DocumentType |
- NodeTypeId::DocumentFragment => Some(self.owner_doc()),
- NodeTypeId::Document(_) => None
+ NodeTypeId::Document(_) => None,
+ _ => Some(self.owner_doc()),
}
}
// https://dom.spec.whatwg.org/#dom-node-getrootnode
- fn GetRootNode(&self) -> Root<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
- fn GetParentNode(&self) -> Option<Root<Node>> {
+ fn GetParentNode(&self) -> Option<DomRoot<Node>> {
self.parent_node.get()
}
// https://dom.spec.whatwg.org/#dom-node-parentelement
- fn GetParentElement(&self) -> Option<Root<Element>> {
- self.GetParentNode().and_then(Root::downcast)
+ fn GetParentElement(&self) -> Option<DomRoot<Element>> {
+ self.GetParentNode().and_then(DomRoot::downcast)
}
// https://dom.spec.whatwg.org/#dom-node-haschildnodes
@@ -1925,7 +2525,7 @@ impl NodeMethods for Node {
}
// https://dom.spec.whatwg.org/#dom-node-childnodes
- fn ChildNodes(&self) -> Root<NodeList> {
+ fn ChildNodes(&self) -> DomRoot<NodeList> {
self.child_list.or_init(|| {
let doc = self.owner_doc();
let window = doc.window();
@@ -1934,53 +2534,65 @@ impl NodeMethods for Node {
}
// https://dom.spec.whatwg.org/#dom-node-firstchild
- fn GetFirstChild(&self) -> Option<Root<Node>> {
+ fn GetFirstChild(&self) -> Option<DomRoot<Node>> {
self.first_child.get()
}
// https://dom.spec.whatwg.org/#dom-node-lastchild
- fn GetLastChild(&self) -> Option<Root<Node>> {
+ fn GetLastChild(&self) -> Option<DomRoot<Node>> {
self.last_child.get()
}
// https://dom.spec.whatwg.org/#dom-node-previoussibling
- fn GetPreviousSibling(&self) -> Option<Root<Node>> {
+ fn GetPreviousSibling(&self) -> Option<DomRoot<Node>> {
self.prev_sibling.get()
}
// https://dom.spec.whatwg.org/#dom-node-nextsibling
- fn GetNextSibling(&self) -> Option<Root<Node>> {
+ fn GetNextSibling(&self) -> Option<DomRoot<Node>> {
self.next_sibling.get()
}
// https://dom.spec.whatwg.org/#dom-node-nodevalue
fn GetNodeValue(&self) -> Option<DOMString> {
- self.downcast::<CharacterData>().map(CharacterData::Data)
+ match self.type_id() {
+ NodeTypeId::Attr => Some(self.downcast::<Attr>().unwrap().Value()),
+ NodeTypeId::CharacterData(_) => {
+ self.downcast::<CharacterData>().map(CharacterData::Data)
+ },
+ _ => None,
+ }
}
// https://dom.spec.whatwg.org/#dom-node-nodevalue
fn SetNodeValue(&self, val: Option<DOMString>) {
- if let Some(character_data) = self.downcast::<CharacterData>() {
- character_data.SetData(val.unwrap_or_default());
+ match self.type_id() {
+ NodeTypeId::Attr => {
+ let attr = self.downcast::<Attr>().unwrap();
+ attr.SetValue(val.unwrap_or_default());
+ },
+ NodeTypeId::CharacterData(_) => {
+ let character_data = self.downcast::<CharacterData>().unwrap();
+ character_data.SetData(val.unwrap_or_default());
+ },
+ _ => {},
}
}
// 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::Attr => Some(self.downcast::<Attr>().unwrap().Value()),
NodeTypeId::CharacterData(..) => {
let characterdata = self.downcast::<CharacterData>().unwrap();
Some(characterdata.Data())
- }
- NodeTypeId::DocumentType |
- NodeTypeId::Document(_) => {
- None
- }
+ },
+ NodeTypeId::DocumentType | NodeTypeId::Document(_) => None,
}
}
@@ -1988,45 +2600,47 @@ 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
} else {
- Some(Root::upcast(self.owner_doc().CreateTextNode(value)))
+ Some(DomRoot::upcast(self.owner_doc().CreateTextNode(value)))
};
// Step 3.
- Node::replace_all(node.r(), self);
- }
+ Node::replace_all(node.as_deref(), self);
+ },
+ NodeTypeId::Attr => {
+ let attr = self.downcast::<Attr>().unwrap();
+ attr.SetValue(value);
+ },
NodeTypeId::CharacterData(..) => {
let characterdata = self.downcast::<CharacterData>().unwrap();
characterdata.SetData(value);
- }
- NodeTypeId::DocumentType |
- NodeTypeId::Document(_) => {}
+ },
+ NodeTypeId::DocumentType | NodeTypeId::Document(_) => {},
}
}
// https://dom.spec.whatwg.org/#dom-node-insertbefore
- fn InsertBefore(&self, node: &Node, child: Option<&Node>) -> Fallible<Root<Node>> {
+ fn InsertBefore(&self, node: &Node, child: Option<&Node>) -> Fallible<DomRoot<Node>> {
Node::pre_insert(node, self, child)
}
// https://dom.spec.whatwg.org/#dom-node-appendchild
- fn AppendChild(&self, node: &Node) -> Fallible<Root<Node>> {
+ fn AppendChild(&self, node: &Node) -> Fallible<DomRoot<Node>> {
Node::pre_insert(node, self, None)
}
// https://dom.spec.whatwg.org/#concept-node-replace
- fn ReplaceChild(&self, node: &Node, child: &Node) -> Fallible<Root<Node>> {
+ fn ReplaceChild(&self, node: &Node, child: &Node) -> Fallible<DomRoot<Node>> {
// Step 1.
match self.type_id() {
- NodeTypeId::Document(_) |
- NodeTypeId::DocumentFragment |
- NodeTypeId::Element(..) => (),
- _ => return Err(Error::HierarchyRequest)
+ NodeTypeId::Document(_) | NodeTypeId::DocumentFragment(_) | NodeTypeId::Element(..) => {
+ ()
+ },
+ _ => return Err(Error::HierarchyRequest),
}
// Step 2.
@@ -2041,22 +2655,23 @@ impl NodeMethods for Node {
// Step 4-5.
match node.type_id() {
- NodeTypeId::CharacterData(CharacterDataTypeId::Text) if self.is::<Document>() =>
- return Err(Error::HierarchyRequest),
- NodeTypeId::DocumentType if !self.is::<Document>() => return Err(Error::HierarchyRequest),
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) if self.is::<Document>() => {
+ return Err(Error::HierarchyRequest);
+ },
+ NodeTypeId::DocumentType if !self.is::<Document>() => {
+ return Err(Error::HierarchyRequest);
+ },
NodeTypeId::Document(_) => return Err(Error::HierarchyRequest),
- _ => ()
+ _ => (),
}
// Step 6.
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>())
- {
+ if node.children().any(|c| c.is::<Text>()) {
return Err(Error::HierarchyRequest);
}
match node.child_elements().count() {
@@ -2066,54 +2681,49 @@ impl NodeMethods for Node {
if self.child_elements().any(|c| c.upcast::<Node>() != child) {
return Err(Error::HierarchyRequest);
}
- if child.following_siblings()
- .any(|child| child.is_doctype()) {
+ if child.following_siblings().any(|child| child.is_doctype()) {
return Err(Error::HierarchyRequest);
}
},
// Step 6.1.1(a)
- _ => return Err(Error::HierarchyRequest)
+ _ => return Err(Error::HierarchyRequest),
}
},
// Step 6.2
NodeTypeId::Element(..) => {
- if self.child_elements()
- .any(|c| c.upcast::<Node>() != child) {
+ if self.child_elements().any(|c| c.upcast::<Node>() != child) {
return Err(Error::HierarchyRequest);
}
- if child.following_siblings()
- .any(|child| child.is_doctype())
- {
+ if child.following_siblings().any(|child| child.is_doctype()) {
return Err(Error::HierarchyRequest);
}
},
// Step 6.3
NodeTypeId::DocumentType => {
- if self.children()
- .any(|c| c.is_doctype() &&
- &*c != child)
- {
+ if self.children().any(|c| c.is_doctype() && &*c != child) {
return Err(Error::HierarchyRequest);
}
- if self.children()
- .take_while(|c| &**c != child)
- .any(|c| c.is::<Element>())
+ if self
+ .children()
+ .take_while(|c| &**c != child)
+ .any(|c| c.is::<Element>())
{
return Err(Error::HierarchyRequest);
}
},
NodeTypeId::CharacterData(..) => (),
NodeTypeId::Document(_) => unreachable!(),
+ NodeTypeId::Attr => unreachable!(),
}
}
// Step 7-8.
let child_next_sibling = child.GetNextSibling();
let node_next_sibling = node.GetNextSibling();
- let reference_child = if child_next_sibling.r() == Some(node) {
- node_next_sibling.r()
+ let reference_child = if child_next_sibling.as_deref() == Some(node) {
+ node_next_sibling.as_deref()
} else {
- child_next_sibling.r()
+ child_next_sibling.as_deref()
};
// Step 9.
@@ -2133,29 +2743,42 @@ impl NodeMethods for Node {
// Step 12.
rooted_vec!(let mut nodes);
- let nodes = if node.type_id() == NodeTypeId::DocumentFragment {
- nodes.extend(node.children().map(|node| JS::from_ref(&*node)));
+ 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 {
- ref_slice(&node)
+ from_ref(&node)
};
// Step 13.
Node::insert(node, self, reference_child, SuppressObserver::Suppressed);
// Step 14.
- vtable_for(&self).children_changed(
- &ChildrenMutation::replace(previous_sibling.r(),
- &removed_child, nodes,
- reference_child));
+ vtable_for(&self).children_changed(&ChildrenMutation::replace(
+ previous_sibling.as_deref(),
+ &removed_child,
+ nodes,
+ reference_child,
+ ));
+ let removed = removed_child.map(|r| [r]);
+ let mutation = Mutation::ChildList {
+ added: Some(nodes),
+ removed: removed.as_ref().map(|r| &r[..]),
+ prev: previous_sibling.as_deref(),
+ next: reference_child,
+ };
+
+ MutationObserver::queue_a_mutation_record(&self, mutation);
// Step 15.
- Ok(Root::from_ref(child))
+ Ok(DomRoot::from_ref(child))
}
// https://dom.spec.whatwg.org/#dom-node-removechild
- fn RemoveChild(&self, node: &Node)
- -> Fallible<Root<Node>> {
+ fn RemoveChild(&self, node: &Node) -> Fallible<DomRoot<Node>> {
Node::pre_remove(node, self)
}
@@ -2170,10 +2793,16 @@ impl NodeMethods for Node {
Node::remove(&node, self, SuppressObserver::Unsuppressed);
continue;
}
- while children.peek().map_or(false, |&(_, ref sibling)| sibling.is::<Text>()) {
+ while children
+ .peek()
+ .map_or(false, |&(_, ref sibling)| sibling.is::<Text>())
+ {
let (index, sibling) = children.next().unwrap();
- sibling.ranges.drain_to_preceding_text_sibling(&sibling, &node, length);
- self.ranges.move_to_text_child_at(self, index as u32, &node, length as u32);
+ sibling
+ .ranges
+ .drain_to_preceding_text_sibling(&sibling, &node, length);
+ self.ranges
+ .move_to_text_child_at(self, index as u32, &node, length as u32);
let sibling_cdata = sibling.downcast::<CharacterData>().unwrap();
length += sibling_cdata.Length();
cdata.append_data(&sibling_cdata.data());
@@ -2186,12 +2815,19 @@ impl NodeMethods for Node {
}
// https://dom.spec.whatwg.org/#dom-node-clonenode
- fn CloneNode(&self, deep: bool) -> Root<Node> {
- Node::clone(self, None, if deep {
- CloneChildrenFlag::CloneChildren
- } else {
- CloneChildrenFlag::DoNotCloneChildren
- })
+ fn CloneNode(&self, deep: bool) -> Fallible<DomRoot<Node>> {
+ if deep && self.is::<ShadowRoot>() {
+ return Err(Error::NotSupported);
+ }
+ Ok(Node::clone(
+ self,
+ None,
+ if deep {
+ CloneChildrenFlag::CloneChildren
+ } else {
+ CloneChildrenFlag::DoNotCloneChildren
+ },
+ ))
}
// https://dom.spec.whatwg.org/#dom-node-isequalnode
@@ -2200,28 +2836,36 @@ impl NodeMethods for Node {
let doctype = node.downcast::<DocumentType>().unwrap();
let other_doctype = other.downcast::<DocumentType>().unwrap();
(*doctype.name() == *other_doctype.name()) &&
- (*doctype.public_id() == *other_doctype.public_id()) &&
- (*doctype.system_id() == *other_doctype.system_id())
+ (*doctype.public_id() == *other_doctype.public_id()) &&
+ (*doctype.system_id() == *other_doctype.system_id())
}
fn is_equal_element(node: &Node, other: &Node) -> bool {
let element = node.downcast::<Element>().unwrap();
let other_element = other.downcast::<Element>().unwrap();
(*element.namespace() == *other_element.namespace()) &&
- (element.prefix() == other_element.prefix()) &&
- (*element.local_name() == *other_element.local_name()) &&
- (element.attrs().len() == other_element.attrs().len())
+ (*element.prefix() == *other_element.prefix()) &&
+ (*element.local_name() == *other_element.local_name()) &&
+ (element.attrs().len() == other_element.attrs().len())
}
fn is_equal_processinginstruction(node: &Node, other: &Node) -> bool {
let pi = node.downcast::<ProcessingInstruction>().unwrap();
let other_pi = other.downcast::<ProcessingInstruction>().unwrap();
(*pi.target() == *other_pi.target()) &&
- (*pi.upcast::<CharacterData>().data() == *other_pi.upcast::<CharacterData>().data())
+ (*pi.upcast::<CharacterData>().data() ==
+ *other_pi.upcast::<CharacterData>().data())
}
fn is_equal_characterdata(node: &Node, other: &Node) -> bool {
let characterdata = node.downcast::<CharacterData>().unwrap();
let other_characterdata = other.downcast::<CharacterData>().unwrap();
*characterdata.data() == *other_characterdata.data()
}
+ fn is_equal_attr(node: &Node, other: &Node) -> bool {
+ let attr = node.downcast::<Attr>().unwrap();
+ let other_attr = other.downcast::<Attr>().unwrap();
+ (*attr.namespace() == *other_attr.namespace()) &&
+ (attr.local_name() == other_attr.local_name()) &&
+ (**attr.value() == **other_attr.value())
+ }
fn is_equal_element_attrs(node: &Node, other: &Node) -> bool {
let element = node.downcast::<Element>().unwrap();
let other_element = other.downcast::<Element>().unwrap();
@@ -2229,11 +2873,12 @@ impl NodeMethods for Node {
element.attrs().iter().all(|attr| {
other_element.attrs().iter().any(|other_attr| {
(*attr.namespace() == *other_attr.namespace()) &&
- (attr.local_name() == other_attr.local_name()) &&
- (**attr.value() == **other_attr.value())
+ (attr.local_name() == other_attr.local_name()) &&
+ (**attr.value() == **other_attr.value())
})
})
}
+
fn is_equal_node(this: &Node, node: &Node) -> bool {
// Step 2.
if this.NodeType() != node.NodeType() {
@@ -2242,19 +2887,24 @@ impl NodeMethods for Node {
match node.type_id() {
// Step 3.
- NodeTypeId::DocumentType
- if !is_equal_doctype(this, node) => return false,
- NodeTypeId::Element(..)
- if !is_equal_element(this, node) => return false,
+ NodeTypeId::DocumentType if !is_equal_doctype(this, node) => return false,
+ NodeTypeId::Element(..) if !is_equal_element(this, node) => return false,
NodeTypeId::CharacterData(CharacterDataTypeId::ProcessingInstruction)
- if !is_equal_processinginstruction(this, node) => return false,
- NodeTypeId::CharacterData(CharacterDataTypeId::Text) |
+ if !is_equal_processinginstruction(this, node) =>
+ {
+ return false;
+ }
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) |
NodeTypeId::CharacterData(CharacterDataTypeId::Comment)
- if !is_equal_characterdata(this, node) => return false,
+ if !is_equal_characterdata(this, node) =>
+ {
+ return false;
+ }
// Step 4.
- NodeTypeId::Element(..)
- if !is_equal_element_attrs(this, node) => return false,
- _ => ()
+ NodeTypeId::Element(..) if !is_equal_element_attrs(this, node) => return false,
+ NodeTypeId::Attr if !is_equal_attr(this, node) => return false,
+
+ _ => (),
}
// Step 5.
@@ -2263,15 +2913,15 @@ impl NodeMethods for Node {
}
// Step 6.
- this.children().zip(node.children()).all(|(child, other_child)| {
- is_equal_node(&child, &other_child)
- })
+ this.children()
+ .zip(node.children())
+ .all(|(child, other_child)| is_equal_node(&child, &other_child))
}
match maybe_node {
// Step 1.
None => false,
// Step 2-6.
- Some(node) => is_equal_node(self, node)
+ Some(node) => is_equal_node(self, node),
}
}
@@ -2285,55 +2935,158 @@ impl NodeMethods for Node {
// https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
fn CompareDocumentPosition(&self, other: &Node) -> u16 {
+ // step 1.
if self == other {
- // step 2.
- 0
- } else {
- let mut lastself = Root::from_ref(self);
- let mut lastother = Root::from_ref(other);
- for ancestor in self.ancestors() {
- if &*ancestor == other {
- // step 4.
- return NodeConstants::DOCUMENT_POSITION_CONTAINS +
- NodeConstants::DOCUMENT_POSITION_PRECEDING;
- }
- lastself = ancestor;
+ return 0;
+ }
+
+ // step 2
+ let mut node1 = Some(other);
+ let mut node2 = Some(self);
+
+ // step 3
+ let mut attr1: Option<&Attr> = None;
+ let mut attr2: Option<&Attr> = None;
+
+ // step 4: spec says to operate on node1 here,
+ // node1 is definitely Some(other) going into this step
+ // The compiler doesn't know the lifetime of attr1.GetOwnerElement
+ // is guaranteed by the lifetime of attr1, so we hold it explicitly
+ let attr1owner;
+ if let Some(ref a) = other.downcast::<Attr>() {
+ attr1 = Some(a);
+ attr1owner = a.GetOwnerElement();
+ node1 = match attr1owner {
+ Some(ref e) => Some(&e.upcast()),
+ None => None,
}
- for ancestor in other.ancestors() {
- if &*ancestor == self {
- // step 5.
- return NodeConstants::DOCUMENT_POSITION_CONTAINED_BY +
- NodeConstants::DOCUMENT_POSITION_FOLLOWING;
- }
- lastother = ancestor;
+ }
+
+ // step 5.1: spec says to operate on node2 here,
+ // node2 is definitely just Some(self) going into this step
+ let attr2owner;
+ if let Some(ref a) = self.downcast::<Attr>() {
+ attr2 = Some(a);
+ attr2owner = a.GetOwnerElement();
+ node2 = match attr2owner {
+ Some(ref e) => Some(&*e.upcast()),
+ None => None,
}
+ }
- if lastself != lastother {
- let abstract_uint: uintptr_t = as_uintptr(&self);
- let other_uint: uintptr_t = as_uintptr(&*other);
+ // Step 5.2
+ // This substep seems lacking in test coverage.
+ // We hit this when comparing two attributes that have the
+ // same owner element.
+ if let Some(node2) = node2 {
+ if Some(node2) == node1 {
+ match (attr1, attr2) {
+ (Some(a1), Some(a2)) => {
+ let attrs = node2.downcast::<Element>().unwrap().attrs();
+ // go through the attrs in order to see if self
+ // or other is first; spec is clear that we
+ // want value-equality, not reference-equality
+ for attr in attrs.iter() {
+ if (*attr.namespace() == *a1.namespace()) &&
+ (attr.local_name() == a1.local_name()) &&
+ (**attr.value() == **a1.value())
+ {
+ return NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
+ NodeConstants::DOCUMENT_POSITION_PRECEDING;
+ }
+ if (*attr.namespace() == *a2.namespace()) &&
+ (attr.local_name() == a2.local_name()) &&
+ (**attr.value() == **a2.value())
+ {
+ return NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING;
+ }
+ }
+ // both attrs have node2 as their owner element, so
+ // we can't have left the loop without seeing them
+ unreachable!();
+ },
+ (_, _) => {},
+ }
+ }
+ }
- let random = if abstract_uint < other_uint {
- NodeConstants::DOCUMENT_POSITION_FOLLOWING
- } else {
- NodeConstants::DOCUMENT_POSITION_PRECEDING
- };
- // step 3.
- return random +
+ // Step 6
+ match (node1, node2) {
+ (None, _) => {
+ // node1 is null
+ return NodeConstants::DOCUMENT_POSITION_FOLLOWING +
NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
- }
-
- for child in lastself.traverse_preorder() {
- if &*child == other {
- // step 6.
- return NodeConstants::DOCUMENT_POSITION_PRECEDING;
+ },
+ (_, None) => {
+ // node2 is null
+ return NodeConstants::DOCUMENT_POSITION_PRECEDING +
+ NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
+ NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
+ },
+ (Some(node1), Some(node2)) => {
+ // still step 6, testing if node1 and 2 share a root
+ let mut self_and_ancestors = node2
+ .inclusive_ancestors(ShadowIncluding::No)
+ .collect::<SmallVec<[_; 20]>>();
+ let mut other_and_ancestors = node1
+ .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()) <
+ as_uintptr(other_and_ancestors.last().unwrap());
+ let random = if random {
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING
+ } else {
+ NodeConstants::DOCUMENT_POSITION_PRECEDING
+ };
+
+ // Disconnected.
+ return random +
+ NodeConstants::DOCUMENT_POSITION_DISCONNECTED +
+ NodeConstants::DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
}
- if &*child == self {
- // step 7.
- return NodeConstants::DOCUMENT_POSITION_FOLLOWING;
+ // steps 7-10
+ let mut parent = self_and_ancestors.pop().unwrap();
+ other_and_ancestors.pop().unwrap();
+
+ let mut current_position =
+ cmp::min(self_and_ancestors.len(), other_and_ancestors.len());
+
+ while current_position > 0 {
+ current_position -= 1;
+ let child_1 = self_and_ancestors.pop().unwrap();
+ let child_2 = other_and_ancestors.pop().unwrap();
+
+ if child_1 != child_2 {
+ let is_before = parent.children().position(|c| c == child_1).unwrap() <
+ parent.children().position(|c| c == child_2).unwrap();
+ // If I am before, `other` is following, and the other way
+ // around.
+ return if is_before {
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING
+ } else {
+ NodeConstants::DOCUMENT_POSITION_PRECEDING
+ };
+ }
+
+ parent = child_1;
}
- }
- unreachable!()
+
+ // We hit the end of one of the parent chains, so one node needs to be
+ // contained in the other.
+ //
+ // If we're the container, return that `other` is contained by us.
+ return if self_and_ancestors.len() < other_and_ancestors.len() {
+ NodeConstants::DOCUMENT_POSITION_FOLLOWING +
+ NodeConstants::DOCUMENT_POSITION_CONTAINED_BY
+ } else {
+ NodeConstants::DOCUMENT_POSITION_PRECEDING +
+ NodeConstants::DOCUMENT_POSITION_CONTAINS
+ };
+ },
}
}
@@ -2341,7 +3094,7 @@ impl NodeMethods for Node {
fn Contains(&self, maybe_other: Option<&Node>) -> bool {
match maybe_other {
None => false,
- Some(other) => self.is_inclusive_ancestor_of(other)
+ Some(other) => self.is_inclusive_ancestor_of(other),
}
}
@@ -2356,20 +3109,21 @@ impl NodeMethods for Node {
// Step 2.
match self.type_id() {
- NodeTypeId::Element(..) => {
- self.downcast::<Element>().unwrap().lookup_prefix(namespace)
- },
- NodeTypeId::Document(_) => {
- self.downcast::<Document>().unwrap().GetDocumentElement().and_then(|element| {
- element.lookup_prefix(namespace)
- })
- },
- NodeTypeId::DocumentType | NodeTypeId::DocumentFragment => None,
- _ => {
- self.GetParentElement().and_then(|element| {
- element.lookup_prefix(namespace)
- })
- }
+ NodeTypeId::Element(..) => self.downcast::<Element>().unwrap().lookup_prefix(namespace),
+ NodeTypeId::Document(_) => self
+ .downcast::<Document>()
+ .unwrap()
+ .GetDocumentElement()
+ .and_then(|element| element.lookup_prefix(namespace)),
+ NodeTypeId::DocumentType | NodeTypeId::DocumentFragment(_) => None,
+ NodeTypeId::Attr => self
+ .downcast::<Attr>()
+ .unwrap()
+ .GetOwnerElement()
+ .and_then(|element| element.lookup_prefix(namespace)),
+ _ => self
+ .GetParentElement()
+ .and_then(|element| element.lookup_prefix(namespace)),
}
}
@@ -2378,7 +3132,7 @@ impl NodeMethods for Node {
// Step 1.
let prefix = match prefix {
Some(ref p) if p.is_empty() => None,
- pre => pre
+ pre => pre,
};
// Step 2.
@@ -2394,18 +3148,35 @@ impl NodeMethods for Node {
}
}
-pub fn document_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> Root<Document> {
+pub fn document_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> DomRoot<Document> {
derived.upcast().owner_doc()
}
-pub fn window_from_node<T: DerivedFrom<Node> + DomObject>(derived: &T) -> Root<Window> {
+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);
- Root::from_ref(document.window())
+ DomRoot::from_ref(document.window())
}
impl VirtualMethods for Node {
- fn super_type(&self) -> Option<&VirtualMethods> {
- Some(self.upcast::<EventTarget>() as &VirtualMethods)
+ fn super_type(&self) -> Option<&dyn VirtualMethods> {
+ Some(self.upcast::<EventTarget>() as &dyn VirtualMethods)
}
fn children_changed(&self, mutation: &ChildrenMutation) {
@@ -2415,6 +3186,7 @@ impl VirtualMethods for Node {
if let Some(list) = self.child_list.get() {
list.as_children_list().children_changed(mutation);
}
+ self.owner_doc().content_and_heritage_changed(self);
}
// This handles the ranges mentioned in steps 2-3 when removing a node.
@@ -2426,7 +3198,7 @@ impl VirtualMethods for Node {
}
/// A summary of the changes that happened to a node.
-#[derive(Copy, Clone, PartialEq, HeapSizeOf)]
+#[derive(Clone, Copy, MallocSizeOf, PartialEq)]
pub enum NodeDamage {
/// The node's `style` attribute changed.
NodeStyleDamaged,
@@ -2435,46 +3207,73 @@ pub enum NodeDamage {
}
pub enum ChildrenMutation<'a> {
- Append { prev: &'a Node, added: &'a [&'a Node] },
- Insert { prev: &'a Node, added: &'a [&'a Node], next: &'a Node },
- Prepend { added: &'a [&'a Node], next: &'a Node },
+ Append {
+ prev: &'a Node,
+ added: &'a [&'a Node],
+ },
+ Insert {
+ prev: &'a Node,
+ added: &'a [&'a Node],
+ next: &'a Node,
+ },
+ Prepend {
+ added: &'a [&'a Node],
+ next: &'a Node,
+ },
Replace {
prev: Option<&'a Node>,
removed: &'a Node,
added: &'a [&'a Node],
next: Option<&'a Node>,
},
- ReplaceAll { removed: &'a [&'a Node], added: &'a [&'a Node] },
+ ReplaceAll {
+ removed: &'a [&'a Node],
+ added: &'a [&'a Node],
+ },
+ /// Mutation for when a Text node's data is modified.
+ /// This doesn't change the structure of the list, which is what the other
+ /// variants' fields are stored for at the moment, so this can just have no
+ /// fields.
+ ChangeText,
}
impl<'a> ChildrenMutation<'a> {
- fn insert(prev: Option<&'a Node>, added: &'a [&'a Node], next: Option<&'a Node>)
- -> ChildrenMutation<'a> {
+ fn insert(
+ prev: Option<&'a Node>,
+ added: &'a [&'a Node],
+ next: Option<&'a Node>,
+ ) -> ChildrenMutation<'a> {
match (prev, next) {
- (None, None) => {
- ChildrenMutation::ReplaceAll { removed: &[], added: added }
+ (None, None) => ChildrenMutation::ReplaceAll {
+ removed: &[],
+ added: added,
},
- (Some(prev), None) => {
- ChildrenMutation::Append { prev: prev, added: added }
+ (Some(prev), None) => ChildrenMutation::Append {
+ prev: prev,
+ added: added,
},
- (None, Some(next)) => {
- ChildrenMutation::Prepend { added: added, next: next }
+ (None, Some(next)) => ChildrenMutation::Prepend {
+ added: added,
+ next: next,
},
- (Some(prev), Some(next)) => {
- ChildrenMutation::Insert { prev: prev, added: added, next: next }
+ (Some(prev), Some(next)) => ChildrenMutation::Insert {
+ prev: prev,
+ added: added,
+ next: next,
},
}
}
- fn replace(prev: Option<&'a Node>,
- removed: &'a Option<&'a Node>,
- added: &'a [&'a Node],
- next: Option<&'a Node>)
- -> ChildrenMutation<'a> {
+ fn replace(
+ prev: Option<&'a Node>,
+ removed: &'a Option<&'a Node>,
+ added: &'a [&'a Node],
+ next: Option<&'a Node>,
+ ) -> ChildrenMutation<'a> {
if let Some(ref removed) = *removed {
if let (None, None) = (prev, next) {
ChildrenMutation::ReplaceAll {
- removed: ref_slice(removed),
+ removed: from_ref(removed),
added: added,
}
} else {
@@ -2490,12 +3289,17 @@ impl<'a> ChildrenMutation<'a> {
}
}
- fn replace_all(removed: &'a [&'a Node], added: &'a [&'a Node])
- -> ChildrenMutation<'a> {
- ChildrenMutation::ReplaceAll { removed: removed, added: added }
+ fn replace_all(removed: &'a [&'a Node], added: &'a [&'a Node]) -> ChildrenMutation<'a> {
+ ChildrenMutation::ReplaceAll {
+ removed: removed,
+ added: added,
+ }
}
/// Get the child that follows the added or removed children.
+ /// Currently only used when this mutation might force us to
+ /// restyle later children (see HAS_SLOW_SELECTOR_LATER_SIBLINGS and
+ /// Element's implementation of VirtualMethods::children_changed).
pub fn next_child(&self) -> Option<&Node> {
match *self {
ChildrenMutation::Append { .. } => None,
@@ -2503,6 +3307,7 @@ impl<'a> ChildrenMutation<'a> {
ChildrenMutation::Prepend { next, .. } => Some(next),
ChildrenMutation::Replace { next, .. } => next,
ChildrenMutation::ReplaceAll { .. } => None,
+ ChildrenMutation::ChangeText => None,
}
}
@@ -2512,38 +3317,75 @@ impl<'a> ChildrenMutation<'a> {
/// NOTE: This does not check whether the inserted/removed nodes were elements, so in some
/// cases it will return a false positive. This doesn't matter for correctness, because at
/// worst the returned element will be restyled unnecessarily.
- pub fn modified_edge_element(&self) -> Option<Root<Node>> {
+ pub fn modified_edge_element(&self) -> Option<DomRoot<Node>> {
match *self {
// Add/remove at start of container: Return the first following element.
ChildrenMutation::Prepend { next, .. } |
- ChildrenMutation::Replace { prev: None, next: Some(next), .. } => {
- next.inclusively_following_siblings().filter(|node| node.is::<Element>()).next()
- }
+ ChildrenMutation::Replace {
+ prev: None,
+ next: Some(next),
+ ..
+ } => next
+ .inclusively_following_siblings()
+ .filter(|node| node.is::<Element>())
+ .next(),
// Add/remove at end of container: Return the last preceding element.
ChildrenMutation::Append { prev, .. } |
- ChildrenMutation::Replace { prev: Some(prev), next: None, .. } => {
- prev.inclusively_preceding_siblings().filter(|node| node.is::<Element>()).next()
- }
+ ChildrenMutation::Replace {
+ prev: Some(prev),
+ next: None,
+ ..
+ } => prev
+ .inclusively_preceding_siblings()
+ .filter(|node| node.is::<Element>())
+ .next(),
// Insert or replace in the middle:
ChildrenMutation::Insert { prev, next, .. } |
- ChildrenMutation::Replace { prev: Some(prev), next: Some(next), .. } => {
- if prev.inclusively_preceding_siblings().all(|node| !node.is::<Element>()) {
+ ChildrenMutation::Replace {
+ prev: Some(prev),
+ next: Some(next),
+ ..
+ } => {
+ if prev
+ .inclusively_preceding_siblings()
+ .all(|node| !node.is::<Element>())
+ {
// Before the first element: Return the first following element.
- next.inclusively_following_siblings().filter(|node| node.is::<Element>()).next()
- } else if next.inclusively_following_siblings().all(|node| !node.is::<Element>()) {
+ next.inclusively_following_siblings()
+ .filter(|node| node.is::<Element>())
+ .next()
+ } else if next
+ .inclusively_following_siblings()
+ .all(|node| !node.is::<Element>())
+ {
// After the last element: Return the last preceding element.
- prev.inclusively_preceding_siblings().filter(|node| node.is::<Element>()).next()
+ prev.inclusively_preceding_siblings()
+ .filter(|node| node.is::<Element>())
+ .next()
} else {
None
}
- }
+ },
- ChildrenMutation::Replace { prev: None, next: None, .. } => unreachable!(),
+ ChildrenMutation::Replace {
+ prev: None,
+ next: None,
+ ..
+ } => unreachable!(),
ChildrenMutation::ReplaceAll { .. } => None,
+ ChildrenMutation::ChangeText => None,
}
}
}
+/// 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> {
@@ -2553,19 +3395,28 @@ pub struct UnbindContext<'a> {
pub parent: &'a Node,
/// The previous sibling of the inclusive ancestor that was removed.
prev_sibling: Option<&'a Node>,
- /// Whether the tree is in a document.
+ /// The next sibling of the inclusive ancestor that was removed.
+ pub next_sibling: Option<&'a Node>,
+ /// Whether the tree is connected.
+ pub tree_connected: bool,
+ /// Whether the tree is in doc.
pub tree_in_doc: bool,
}
impl<'a> UnbindContext<'a> {
/// Create a new `UnbindContext` value.
- fn new(parent: &'a Node,
- prev_sibling: Option<&'a Node>,
- cached_index: Option<u32>) -> Self {
+ pub fn new(
+ parent: &'a Node,
+ prev_sibling: Option<&'a Node>,
+ next_sibling: Option<&'a Node>,
+ cached_index: Option<u32>,
+ ) -> Self {
UnbindContext {
index: Cell::new(cached_index),
parent: parent,
prev_sibling: prev_sibling,
+ next_sibling: next_sibling,
+ tree_connected: parent.is_connected(),
tree_in_doc: parent.is_in_doc(),
}
}
@@ -2583,17 +3434,17 @@ impl<'a> UnbindContext<'a> {
}
/// A node's unique ID, for devtools.
-struct UniqueId {
+pub struct UniqueId {
cell: UnsafeCell<Option<Box<Uuid>>>,
}
unsafe_no_jsmanaged_fields!(UniqueId);
-impl HeapSizeOf for UniqueId {
+impl MallocSizeOf for UniqueId {
#[allow(unsafe_code)]
- fn heap_size_of_children(&self) -> usize {
+ fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
if let &Some(ref uuid) = unsafe { &*self.cell.get() } {
- unsafe { heap_size_of(&** uuid as *const Uuid as *const _) }
+ unsafe { ops.malloc_size_of(&**uuid) }
} else {
0
}
@@ -2603,7 +3454,9 @@ impl HeapSizeOf for UniqueId {
impl UniqueId {
/// Create a new `UniqueId` value. The underlying `Uuid` is lazily created.
fn new() -> UniqueId {
- UniqueId { cell: UnsafeCell::new(None) }
+ UniqueId {
+ cell: UnsafeCell::new(None),
+ }
}
/// The Uuid of that unique ID.
@@ -2612,7 +3465,7 @@ impl UniqueId {
unsafe {
let ptr = self.cell.get();
if (*ptr).is_none() {
- *ptr = Some(box Uuid::new_v4());
+ *ptr = Some(Box::new(Uuid::new_v4()));
}
&(&*ptr).as_ref().unwrap()
}
@@ -2623,10 +3476,8 @@ impl Into<LayoutNodeType> for NodeTypeId {
#[inline(always)]
fn into(self) -> LayoutNodeType {
match self {
- NodeTypeId::Element(e) =>
- LayoutNodeType::Element(e.into()),
- NodeTypeId::CharacterData(CharacterDataTypeId::Text) =>
- LayoutNodeType::Text,
+ NodeTypeId::Element(e) => LayoutNodeType::Element(e.into()),
+ NodeTypeId::CharacterData(CharacterDataTypeId::Text(_)) => LayoutNodeType::Text,
x => unreachable!("Layout should not traverse nodes of type {:?}", x),
}
}
@@ -2636,30 +3487,57 @@ impl Into<LayoutElementType> for ElementTypeId {
#[inline(always)]
fn into(self) -> LayoutElementType {
match self {
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) =>
- LayoutElementType::HTMLCanvasElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) =>
- LayoutElementType::HTMLIFrameElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) =>
- LayoutElementType::HTMLImageElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) =>
- LayoutElementType::HTMLInputElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) =>
- LayoutElementType::HTMLObjectElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement(_)) =>
- LayoutElementType::HTMLTableCellElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) =>
- LayoutElementType::HTMLTableColElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) =>
- LayoutElementType::HTMLTableElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) =>
- LayoutElementType::HTMLTableRowElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) =>
- LayoutElementType::HTMLTableSectionElement,
- ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) =>
- LayoutElementType::HTMLTextAreaElement,
- ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(SVGGraphicsElementTypeId::SVGSVGElement)) =>
- LayoutElementType::SVGSVGElement,
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBodyElement) => {
+ LayoutElementType::HTMLBodyElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLBRElement) => {
+ LayoutElementType::HTMLBRElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLCanvasElement) => {
+ LayoutElementType::HTMLCanvasElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLHtmlElement) => {
+ LayoutElementType::HTMLHtmlElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLIFrameElement) => {
+ LayoutElementType::HTMLIFrameElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLImageElement) => {
+ LayoutElementType::HTMLImageElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLMediaElement(_)) => {
+ LayoutElementType::HTMLMediaElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLInputElement) => {
+ LayoutElementType::HTMLInputElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLObjectElement) => {
+ LayoutElementType::HTMLObjectElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLParagraphElement) => {
+ LayoutElementType::HTMLParagraphElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableCellElement) => {
+ LayoutElementType::HTMLTableCellElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableColElement) => {
+ LayoutElementType::HTMLTableColElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableElement) => {
+ LayoutElementType::HTMLTableElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableRowElement) => {
+ LayoutElementType::HTMLTableRowElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTableSectionElement) => {
+ LayoutElementType::HTMLTableSectionElement
+ },
+ ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLTextAreaElement) => {
+ LayoutElementType::HTMLTextAreaElement
+ },
+ ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement(
+ SVGGraphicsElementTypeId::SVGSVGElement,
+ )) => LayoutElementType::SVGSVGElement,
_ => LayoutElementType::Element,
}
}
@@ -2671,8 +3549,9 @@ pub trait VecPreOrderInsertionHelper<T> {
fn insert_pre_order(&mut self, elem: &T, tree_root: &Node);
}
-impl<T> VecPreOrderInsertionHelper<T> for Vec<JS<T>>
- where T: DerivedFrom<Node> + DomObject
+impl<T> VecPreOrderInsertionHelper<T> for Vec<Dom<T>>
+where
+ T: DerivedFrom<Node> + DomObject,
{
/// This algorithm relies on the following assumptions:
/// * any elements inserted in this vector share the same tree root
@@ -2685,21 +3564,21 @@ impl<T> VecPreOrderInsertionHelper<T> for Vec<JS<T>>
/// the traversal.
fn insert_pre_order(&mut self, elem: &T, tree_root: &Node) {
if self.is_empty() {
- self.push(JS::from_ref(elem));
+ self.push(Dom::from_ref(elem));
return;
}
let elem_node = elem.upcast::<Node>();
let mut head: usize = 0;
- for node in tree_root.traverse_preorder() {
- let head_node = Root::upcast::<Node>(Root::from_ref(&*self[head]));
+ 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;
}
- if elem_node == node.r() || head == self.len() {
+ if elem_node == &*node || head == self.len() {
break;
}
}
- self.insert(head, JS::from_ref(elem));
+ self.insert(head, Dom::from_ref(elem));
}
}