Add is_connected flag to node and use it to replace most uses of is_in_doc

This commit is contained in:
Fernando Jiménez Moreno 2019-01-27 17:11:11 +01:00
parent 640fc04743
commit 441357b74e
29 changed files with 111 additions and 96 deletions

View file

@ -1050,7 +1050,7 @@ fn inner_text_collection_steps<N: LayoutNode>(
// Step 3.
let display = style.get_box().display;
if !child.is_in_document() || display == Display::None {
if !child.is_connected() || display == Display::None {
continue;
}

View file

@ -260,8 +260,8 @@ impl<'ln> TNode for ServoLayoutNode<'ln> {
None
}
fn is_in_document(&self) -> bool {
unsafe { self.node.get_flag(NodeFlags::IS_IN_DOC) }
fn is_connected(&self) -> bool {
unsafe { self.node.get_flag(NodeFlags::IS_CONNECTED) }
}
}
@ -488,7 +488,7 @@ impl<'le> TElement for ServoLayoutElement<'le> {
}
unsafe fn set_dirty_descendants(&self) {
debug_assert!(self.as_node().is_in_document());
debug_assert!(self.as_node().is_connected());
self.as_node()
.node
.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true)

View file

@ -246,7 +246,7 @@ impl CSSStyleDeclaration {
},
CSSStyleOwner::Element(ref el) => {
let node = el.upcast::<Node>();
if !node.is_in_doc() {
if !node.is_connected() {
// TODO: Node should be matched against the style rules of this window.
// Firefox is currently the only browser to implement this.
return DOMString::new();

View file

@ -680,7 +680,7 @@ impl Document {
}
pub fn content_and_heritage_changed(&self, node: &Node) {
if node.is_in_doc() {
if node.is_connected() {
node.note_dirty_descendants();
}
@ -749,7 +749,7 @@ impl Document {
"Adding named element to document {:p}: {:p} id={}",
self, element, id
);
assert!(element.upcast::<Node>().is_in_doc());
assert!(element.upcast::<Node>().is_connected());
assert!(!id.is_empty());
let root = self.GetDocumentElement().expect(
@ -2481,12 +2481,12 @@ impl LayoutDocumentHelpers for LayoutDom<Document> {
let mut elements = (*self.unsafe_get())
.pending_restyles
.borrow_mut_for_layout();
// Elements were in a document when they were adding to this list, but that
// Elements were in a document when they were added to this list, but that
// may no longer be true when the next layout occurs.
let result = elements
.drain()
.map(|(k, v)| (k.to_layout(), v))
.filter(|&(ref k, _)| k.upcast::<Node>().get_flag(NodeFlags::IS_IN_DOC))
.filter(|&(ref k, _)| k.upcast::<Node>().get_flag(NodeFlags::IS_CONNECTED))
.collect();
result
}

View file

@ -9,6 +9,7 @@ use crate::dom::attr::{Attr, AttrHelpersForLayout};
use crate::dom::bindings::cell::DomRefCell;
use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
use crate::dom::bindings::codegen::Bindings::DocumentFragmentBinding::DocumentFragmentBinding::DocumentFragmentMethods;
use crate::dom::bindings::codegen::Bindings::ElementBinding;
use crate::dom::bindings::codegen::Bindings::ElementBinding::ElementMethods;
use crate::dom::bindings::codegen::Bindings::EventBinding::EventMethods;
@ -1202,7 +1203,13 @@ impl Element {
}
pub fn root_element(&self) -> DomRoot<Element> {
if self.node.is_in_doc() {
if self.node.is_in_shadow_tree() {
self.upcast::<Node>()
.owner_shadow_root()
.upcast::<DocumentFragment>()
.GetFirstElementChild()
.unwrap()
} else if self.node.is_in_doc() {
self.upcast::<Node>()
.owner_doc()
.GetDocumentElement()
@ -2718,7 +2725,7 @@ impl VirtualMethods for Element {
None
}
});
if node.is_in_doc() {
if node.is_connected() {
let value = attr.value().as_atom().clone();
match mutation {
AttributeMutation::Set(old_value) => {
@ -2764,16 +2771,16 @@ impl VirtualMethods for Element {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
if let Some(f) = self.as_maybe_form_control() {
f.bind_form_control_to_tree();
}
if !tree_in_doc {
if !tree_connected {
return;
}
@ -2792,7 +2799,7 @@ impl VirtualMethods for Element {
f.unbind_form_control_from_tree();
}
if !context.tree_in_doc {
if !context.tree_connected {
return;
}

View file

@ -63,8 +63,8 @@ impl HTMLBaseElement {
/// Update the cached base element in response to binding or unbinding from
/// a tree.
pub fn bind_unbind(&self, tree_in_doc: bool) {
if !tree_in_doc {
pub fn bind_unbind(&self, tree_connected: bool) {
if !tree_connected {
return;
}
@ -119,13 +119,13 @@ impl VirtualMethods for HTMLBaseElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().bind_to_tree(tree_in_doc);
self.bind_unbind(tree_in_doc);
fn bind_to_tree(&self, tree_connected: bool) {
self.super_type().unwrap().bind_to_tree(tree_connected);
self.bind_unbind(tree_connected);
}
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
self.bind_unbind(context.tree_in_doc);
self.bind_unbind(context.tree_connected);
}
}

View file

@ -149,12 +149,12 @@ impl VirtualMethods for HTMLBodyElement {
.attribute_affects_presentational_hints(attr)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
if !tree_in_doc {
if !tree_connected {
return;
}

View file

@ -232,9 +232,9 @@ impl VirtualMethods for HTMLButtonElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
self.upcast::<Element>()

View file

@ -459,7 +459,7 @@ impl HTMLElementMethods for HTMLElement {
let element = self.upcast::<Element>();
// Step 1.
let element_not_rendered = !node.is_in_doc() || !element.has_css_layout_box();
let element_not_rendered = !node.is_connected() || !element.has_css_layout_box();
if element_not_rendered {
return node.GetTextContent().unwrap();
}

View file

@ -1100,7 +1100,7 @@ pub trait FormControl: DomObject {
let form_id = elem.get_string_attribute(&local_name!("form"));
let node = elem.upcast::<Node>();
if self.is_listed() && !form_id.is_empty() && node.is_in_doc() {
if self.is_listed() && !form_id.is_empty() && node.is_connected() {
let doc = document_from_node(node);
doc.register_form_id_listener(form_id, self);
}

View file

@ -81,9 +81,9 @@ impl VirtualMethods for HTMLHeadElement {
fn super_type(&self) -> Option<&dyn VirtualMethods> {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
load_script(self);
}

View file

@ -584,7 +584,7 @@ impl VirtualMethods for HTMLIFrameElement {
// may be in a different script thread. Instread, we check to see if the parent
// is in a document tree and has a browsing context, which is what causes
// the child browsing context to be created.
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
if self.upcast::<Node>().is_connected_with_browsing_context() {
debug!("iframe src set while in browsing context.");
self.process_the_iframe_attributes(ProcessingMode::NotFirstTime);
}
@ -610,9 +610,9 @@ impl VirtualMethods for HTMLIFrameElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
let iframe = Trusted::new(self);
@ -624,9 +624,9 @@ impl VirtualMethods for HTMLIFrameElement {
// browsing context, set the element's nested browsing context
// to the newly-created browsing context, and then process the
// iframe attributes for the "first time"."
if this.upcast::<Node>().is_in_doc_with_browsing_context() {
if this.upcast::<Node>().is_connected_with_browsing_context() {
debug!("iframe bound to browsing context.");
debug_assert!(tree_in_doc, "is_in_doc_with_bc, but not tree_in_doc");
debug_assert!(tree_connected, "is_connected_with_bc, but not tree_connected");
this.create_nested_browsing_context();
this.process_the_iframe_attributes(ProcessingMode::FirstTime);
}

View file

@ -1645,12 +1645,12 @@ impl VirtualMethods for HTMLImageElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
let document = document_from_node(self);
if tree_in_doc {
if tree_connected {
document.register_responsive_image(self);
}

View file

@ -1422,9 +1422,9 @@ impl VirtualMethods for HTMLInputElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
self.upcast::<Element>()
.check_ancestors_disabled_state_for_form_control();

View file

@ -56,9 +56,9 @@ impl VirtualMethods for HTMLLegendElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
self.upcast::<Element>()

View file

@ -183,7 +183,7 @@ impl VirtualMethods for HTMLLinkElement {
fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation) {
self.super_type().unwrap().attribute_mutated(attr, mutation);
if !self.upcast::<Node>().is_in_doc() || mutation.is_removal() {
if !self.upcast::<Node>().is_connected() || mutation.is_removal() {
return;
}
@ -222,12 +222,12 @@ impl VirtualMethods for HTMLLinkElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
if tree_in_doc {
if tree_connected {
let element = self.upcast();
let rel = get_attr(element, &local_name!("rel"));

View file

@ -2009,7 +2009,7 @@ impl VirtualMethods for HTMLMediaElement {
fn unbind_from_tree(&self, context: &UnbindContext) {
self.super_type().unwrap().unbind_from_tree(context);
if context.tree_in_doc {
if context.tree_connected {
let task = MediaElementMicrotask::PauseIfNotInDocumentTask {
elem: DomRoot::from_ref(self),
};
@ -2061,7 +2061,7 @@ impl MicrotaskRunnable for MediaElementMicrotask {
}
},
&MediaElementMicrotask::PauseIfNotInDocumentTask { ref elem } => {
if !elem.upcast::<Node>().is_in_doc() {
if !elem.upcast::<Node>().is_connected() {
elem.internal_pause_steps();
}
},

View file

@ -171,12 +171,12 @@ impl VirtualMethods for HTMLMetaElement {
Some(self.upcast::<HTMLElement>() as &dyn VirtualMethods)
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
if tree_in_doc {
if tree_connected {
self.process_attributes();
}
}
@ -204,7 +204,7 @@ impl VirtualMethods for HTMLMetaElement {
s.unbind_from_tree(context);
}
if context.tree_in_doc {
if context.tree_connected {
self.process_referrer_attribute();
if let Some(s) = self.stylesheet.borrow_mut().take() {

View file

@ -235,9 +235,9 @@ impl VirtualMethods for HTMLOptionElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
self.upcast::<Element>()

View file

@ -372,7 +372,7 @@ impl HTMLScriptElement {
}
// Step 5.
if !self.upcast::<Node>().is_in_doc() {
if !self.upcast::<Node>().is_connected() {
return;
}
@ -760,7 +760,7 @@ impl VirtualMethods for HTMLScriptElement {
match *attr.local_name() {
local_name!("src") => {
if let AttributeMutation::Set(_) = mutation {
if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}
@ -773,7 +773,7 @@ impl VirtualMethods for HTMLScriptElement {
if let Some(ref s) = self.super_type() {
s.children_changed(mutation);
}
if !self.parser_inserted.get() && self.upcast::<Node>().is_in_doc() {
if !self.parser_inserted.get() && self.upcast::<Node>().is_connected() {
self.prepare();
}
}

View file

@ -382,9 +382,9 @@ impl VirtualMethods for HTMLSelectElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
self.upcast::<Element>()

View file

@ -82,8 +82,8 @@ impl VirtualMethods for HTMLSourceElement {
}
/// <https://html.spec.whatwg.org/multipage/#the-source-element:nodes-are-inserted>
fn bind_to_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().bind_to_tree(tree_in_doc);
fn bind_to_tree(&self, tree_connected: bool) {
self.super_type().unwrap().bind_to_tree(tree_connected);
let parent = self.upcast::<Node>().GetParentNode().unwrap();
if let Some(media) = parent.downcast::<HTMLMediaElement>() {
media.handle_source_child_insertion();

View file

@ -185,14 +185,14 @@ impl VirtualMethods for HTMLStyleElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
self.super_type().unwrap().bind_to_tree(tree_in_doc);
fn bind_to_tree(&self, tree_connected: bool) {
self.super_type().unwrap().bind_to_tree(tree_connected);
// https://html.spec.whatwg.org/multipage/#update-a-style-block
// Handles the case when:
// "The element is not on the stack of open elements of an HTML parser or XML parser,
// and it becomes connected or disconnected."
if tree_in_doc && !self.in_stack_of_open_elements.get() {
if tree_connected && !self.in_stack_of_open_elements.get() {
self.parse_own_css();
}
}
@ -214,7 +214,7 @@ impl VirtualMethods for HTMLStyleElement {
s.unbind_from_tree(context);
}
if context.tree_in_doc {
if context.tree_connected {
if let Some(s) = self.stylesheet.borrow_mut().take() {
document_from_node(self).remove_stylesheet(self.upcast(), &s)
}

View file

@ -466,9 +466,9 @@ impl VirtualMethods for HTMLTextAreaElement {
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
self.upcast::<Element>()

View file

@ -69,17 +69,17 @@ impl VirtualMethods for HTMLTitleElement {
s.children_changed(mutation);
}
let node = self.upcast::<Node>();
if node.is_in_doc() {
if node.is_connected() {
node.owner_doc().title_changed();
}
}
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
let node = self.upcast::<Node>();
if tree_in_doc {
if tree_connected {
node.owner_doc().title_changed();
}
}

View file

@ -190,8 +190,11 @@ bitflags! {
/// Whether this element has already handled the stored snapshot.
const HANDLED_SNAPSHOT = 1 << 8;
// Whether this node participates in a shadow tree.
/// 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;
}
}
@ -289,8 +292,9 @@ impl Node {
} else {
false
};
node.set_flag(NodeFlags::IS_IN_DOC, parent_in_doc || is_connected);
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, is_connected);
// Out-of-document elements never have the descendants flag set.
debug_assert!(!node.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS));
vtable_for(&&*node).bind_to_tree(is_connected);
@ -492,6 +496,10 @@ impl Node {
self.flags.get().contains(NodeFlags::IS_IN_SHADOW_TREE)
}
pub fn is_connected(&self) -> bool {
self.flags.get().contains(NodeFlags::IS_CONNECTED)
}
/// Returns the type ID of this node.
pub fn type_id(&self) -> NodeTypeId {
match *self.eventtarget.type_id() {
@ -550,7 +558,7 @@ impl Node {
// FIXME(emilio): This and the function below should move to Element.
pub fn note_dirty_descendants(&self) {
debug_assert!(self.is_in_doc());
debug_assert!(self.is_connected());
for ancestor in self.inclusive_ancestors() {
if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
@ -583,7 +591,7 @@ impl Node {
pub fn dirty(&self, damage: NodeDamage) {
self.rev_version();
if !self.is_in_doc() {
if !self.is_connected() {
return;
}
@ -917,8 +925,8 @@ impl Node {
self.owner_doc().is_html_document()
}
pub fn is_in_doc_with_browsing_context(&self) -> bool {
self.is_in_doc() && self.owner_doc().browsing_context().is_some()
pub fn is_connected_with_browsing_context(&self) -> bool {
self.is_connected() && self.owner_doc().browsing_context().is_some()
}
pub fn children(&self) -> impl Iterator<Item = DomRoot<Node>> {
@ -2915,8 +2923,8 @@ pub struct UnbindContext<'a> {
prev_sibling: Option<&'a Node>,
/// The next sibling of the inclusive ancestor that was removed.
pub next_sibling: Option<&'a Node>,
/// Whether the tree is in a document.
pub tree_in_doc: bool,
/// Whether the tree is connected.
pub tree_connected: bool,
}
impl<'a> UnbindContext<'a> {
@ -2932,7 +2940,7 @@ impl<'a> UnbindContext<'a> {
parent: parent,
prev_sibling: prev_sibling,
next_sibling: next_sibling,
tree_in_doc: parent.is_in_doc(),
tree_connected: parent.is_connected(),
}
}

View file

@ -90,15 +90,15 @@ pub trait VirtualMethods {
}
}
/// Called when a Node is appended to a tree, where 'tree_in_doc' indicates
/// Called when a Node is appended to a tree, where 'tree_connected' indicates
/// whether the tree is part of a Document.
fn bind_to_tree(&self, tree_in_doc: bool) {
fn bind_to_tree(&self, tree_connected: bool) {
if let Some(ref s) = self.super_type() {
s.bind_to_tree(tree_in_doc);
s.bind_to_tree(tree_connected);
}
}
/// Called when a Node is removed from a tree, where 'tree_in_doc'
/// Called when a Node is removed from a tree, where 'tree_connected'
/// indicates whether the tree is part of a Document.
/// Implements removing steps:
/// <https://dom.spec.whatwg.org/#concept-node-remove-ext>

View file

@ -185,8 +185,8 @@ pub trait TNode: Sized + Copy + Clone + Debug + NodeInfo + PartialEq {
DomChildren(self.first_child())
}
/// Returns whether the node is attached to a document.
fn is_in_document(&self) -> bool;
/// Returns whether the node is connected.
fn is_connected(&self) -> bool;
/// Iterate over the DOM children of a node, in preorder.
fn dom_descendants(&self) -> DomDescendants<Self> {

View file

@ -231,7 +231,7 @@ where
// Optimize for when the root is a document or a shadow root and the element
// is connected to that root.
if root.as_document().is_some() {
debug_assert!(element.as_node().is_in_document(), "Not connected?");
debug_assert!(element.as_node().is_connected(), "Not connected?");
debug_assert_eq!(
root,
root.owner_doc().as_node(),
@ -275,10 +275,7 @@ where
return Err(());
}
if root.is_in_document() {
return root.owner_doc().elements_with_id(id);
}
if root.is_connected() {
if let Some(shadow) = root.as_shadow_root() {
return shadow.elements_with_id(id);
}
@ -287,6 +284,9 @@ where
return shadow.elements_with_id(id);
}
return root.owner_doc().elements_with_id(id);
}
Err(())
}