diff options
Diffstat (limited to 'components/script/dom/node.rs')
-rw-r--r-- | components/script/dom/node.rs | 2707 |
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, ©, 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)); } } |