From 851568f59e458d5b24c9a75f5675c366e764d065 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Sat, 10 Dec 2016 17:09:14 -1000 Subject: [PATCH 1/4] Bug 1322945 - Only assert against _restyling_ roots with later siblings. r=heycam The problem is with restyles, so we should leave the door open on initial styling if that ends up making sense. MozReview-Commit-ID: 5GOFBEUZhDe --- components/style/traversal.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 5e0712f2114..1fa35ed3605 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -157,11 +157,11 @@ pub trait DomTraversalContext { // we need a special case for the root. // // Expanding snapshots here may create a LATER_SIBLINGS restyle hint, which - // we will drop on the floor. This is fine, because we don't traverse roots - // with siblings. - debug_assert!(root.next_sibling_element().is_none()); + // we will drop on the floor. To prevent missed restyles, we assert against + // restyling a root with later siblings. if let Some(mut data) = root.mutate_data() { if let Some(r) = data.as_restyle_mut() { + debug_assert!(root.next_sibling_element().is_none()); let _later_siblings = r.expand_snapshot(root, stylist); } } From 75e4c16bc7e1f50a19f52b095d3327462d9c1459 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Sun, 11 Dec 2016 15:42:48 -1000 Subject: [PATCH 2/4] Bug 1322945 - Remove the requirement that the parent styles must be current to style a subtree. r=heycam Sometimes Gecko eagerly styles things without processing pending restyles first. In general we'd like to avoid this, but there can be good reasons (for example, needing to construct a frame for some small piece of newly-added content in order to do something specific with that frame, but not wanting to flush all of layout). Just handle it. MozReview-Commit-ID: EjXs0M4855Q --- components/style/dom.rs | 4 ++-- components/style/matching.rs | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/components/style/dom.rs b/components/style/dom.rs index 8431f5526c3..f32f5e8361e 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -163,10 +163,10 @@ pub trait TElement : PartialEq + Debug + Sized + Copy + Clone + ElementExt + Pre /// traversal. Returns the number of children left to process. fn did_process_child(&self) -> isize; - /// Returns true if this element's style is display:none. + /// Returns true if this element's style is display:none. Panics if + /// the element has no style. fn is_display_none(&self) -> bool { let data = self.borrow_data().unwrap(); - debug_assert!(data.has_current_styles()); data.styles().is_display_none() } diff --git a/components/style/matching.rs b/components/style/matching.rs index 26b563291ba..807deeac209 100644 --- a/components/style/matching.rs +++ b/components/style/matching.rs @@ -733,7 +733,6 @@ pub trait MatchMethods : TElement { // Get our parent's style. let parent_data = parent.as_ref().map(|x| x.borrow_data().unwrap()); let parent_style = parent_data.as_ref().map(|d| { - debug_assert!(d.has_current_styles()); &d.styles().primary.values }); From 3a5695406939dbe370d7cf467d69be7ee6c38759 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Sat, 10 Dec 2016 20:11:36 -1000 Subject: [PATCH 3/4] Bug 1322945 - Change skip_root to unstyled_children_only and use StyleNewChildren in more places. r=heycam I noticed that our current behavior in ContentRangeInserted is incorrect. Unlike ContentInserted (where this code lived originally), ContentRangeInserted takes a start and end element. I'm not sure if we ever take that path for new content that needs style, but it seemed sketchy. And generally, it seems nice to just always style new content the same way (though we still need to style NAC by the subtree root, since it hasn't been attached to the parent yet). For situations where there is indeed only one unstyled child, the traversal overhead should be neglible, since we special-case the single-element in parallel.rs to avoid calling into rayon. Being more explicit about what we want here also makes us more robust against the other handful of callpaths that can take us into nsCSSFrameConstructor::{ContentRangeInserted,ContentAppended}. Currently we can call StyleNewSubtree on an already-styled element via RecreateFramesForContent, which triggers an assertion in the servo traversal. MozReview-Commit-ID: DqCGh90deHH --- components/style/build_gecko.rs | 4 ++-- components/style/gecko_bindings/bindings.rs | 4 ++-- .../style/gecko_bindings/structs_debug.rs | 2 +- .../style/gecko_bindings/structs_release.rs | 2 +- components/style/parallel.rs | 16 +++++++++------ components/style/sequential.rs | 8 ++++++-- components/style/traversal.rs | 20 +++++++++---------- ports/geckolib/glue.rs | 9 +++++---- 8 files changed, 37 insertions(+), 28 deletions(-) diff --git a/components/style/build_gecko.rs b/components/style/build_gecko.rs index a96a92e9871..119d3b30f9d 100644 --- a/components/style/build_gecko.rs +++ b/components/style/build_gecko.rs @@ -220,7 +220,7 @@ mod bindings { "mozilla::ConsumeStyleBehavior", "mozilla::LazyComputeBehavior", "mozilla::css::SheetParsingMode", - "mozilla::SkipRootBehavior", + "mozilla::TraversalRootBehavior", "mozilla::DisplayItemClip", // Needed because bindgen generates // specialization tests for this even // though it shouldn't. @@ -444,7 +444,7 @@ mod bindings { "ThreadSafePrincipalHolder", "ConsumeStyleBehavior", "LazyComputeBehavior", - "SkipRootBehavior", + "TraversalRootBehavior", "FontFamilyList", "FontFamilyType", "ServoElementSnapshot", diff --git a/components/style/gecko_bindings/bindings.rs b/components/style/gecko_bindings/bindings.rs index d7ad23ff955..877dab3ef51 100644 --- a/components/style/gecko_bindings/bindings.rs +++ b/components/style/gecko_bindings/bindings.rs @@ -70,7 +70,7 @@ use gecko_bindings::structs::ThreadSafeURIHolder; use gecko_bindings::structs::ThreadSafePrincipalHolder; use gecko_bindings::structs::ConsumeStyleBehavior; use gecko_bindings::structs::LazyComputeBehavior; -use gecko_bindings::structs::SkipRootBehavior; +use gecko_bindings::structs::TraversalRootBehavior; use gecko_bindings::structs::FontFamilyList; use gecko_bindings::structs::FontFamilyType; use gecko_bindings::structs::ServoElementSnapshot; @@ -1197,7 +1197,7 @@ extern "C" { extern "C" { pub fn Servo_TraverseSubtree(root: RawGeckoElementBorrowed, set: RawServoStyleSetBorrowed, - skip_root: SkipRootBehavior); + behavior: TraversalRootBehavior); } extern "C" { pub fn Servo_AssertTreeIsClean(root: RawGeckoElementBorrowed); diff --git a/components/style/gecko_bindings/structs_debug.rs b/components/style/gecko_bindings/structs_debug.rs index 25960347510..d2081917576 100644 --- a/components/style/gecko_bindings/structs_debug.rs +++ b/components/style/gecko_bindings/structs_debug.rs @@ -2493,7 +2493,7 @@ pub mod root { } #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum SkipRootBehavior { Skip = 0, DontSkip = 1, } + pub enum TraversalRootBehavior { Normal = 0, UnstyledChildrenOnly = 1, } pub mod a11y { #[allow(unused_imports)] use self::super::super::super::root; diff --git a/components/style/gecko_bindings/structs_release.rs b/components/style/gecko_bindings/structs_release.rs index 587d932ba69..c6fdf8d127d 100644 --- a/components/style/gecko_bindings/structs_release.rs +++ b/components/style/gecko_bindings/structs_release.rs @@ -2475,7 +2475,7 @@ pub mod root { } #[repr(i32)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] - pub enum SkipRootBehavior { Skip = 0, DontSkip = 1, } + pub enum TraversalRootBehavior { Normal = 0, UnstyledChildrenOnly = 1, } pub mod a11y { #[allow(unused_imports)] use self::super::super::super::root; diff --git a/components/style/parallel.rs b/components/style/parallel.rs index a93c0006f5f..3bc888a5230 100644 --- a/components/style/parallel.rs +++ b/components/style/parallel.rs @@ -28,13 +28,17 @@ pub fn traverse_dom(root: N::ConcreteElement, STYLE_SHARING_CACHE_MISSES.store(0, Ordering::SeqCst); } - // Handle root skipping. We don't currently support it in conjunction with - // bottom-up traversal. If we did, we'd need to put it on the context to make - // it available to the bottom-up phase. - debug_assert!(!token.should_skip_root() || !C::needs_postorder_traversal()); - let (nodes, depth) = if token.should_skip_root() { + // Handle Gecko's eager initial styling. We don't currently support it + // in conjunction with bottom-up traversal. If we did, we'd need to put + // it on the context to make it available to the bottom-up phase. + let (nodes, depth) = if token.traverse_unstyled_children_only() { + debug_assert!(!C::needs_postorder_traversal()); let mut children = vec![]; - C::traverse_children(root, |kid| children.push(kid.to_unsafe())); + for kid in root.as_node().children() { + if kid.as_element().map_or(false, |el| el.get_data().is_none()) { + children.push(kid.to_unsafe()); + } + } (children, known_root_dom_depth.map(|x| x + 1)) } else { (vec![root.as_node().to_unsafe()], known_root_dom_depth) diff --git a/components/style/sequential.rs b/components/style/sequential.rs index 01632a2d917..614b3d6509d 100644 --- a/components/style/sequential.rs +++ b/components/style/sequential.rs @@ -42,8 +42,12 @@ pub fn traverse_dom(root: N::ConcreteElement, }; let context = C::new(shared, root.as_node().opaque()); - if token.should_skip_root() { - C::traverse_children(root, |kid| doit::(&context, kid, &mut data)); + if token.traverse_unstyled_children_only() { + for kid in root.as_node().children() { + if kid.as_element().map_or(false, |el| el.get_data().is_none()) { + doit::(&context, kid, &mut data); + } + } } else { doit::(&context, root.as_node(), &mut data); } diff --git a/components/style/traversal.rs b/components/style/traversal.rs index 1fa35ed3605..5de8c726348 100644 --- a/components/style/traversal.rs +++ b/components/style/traversal.rs @@ -104,7 +104,7 @@ pub struct PerLevelTraversalData { /// to pass information from the pre-traversal into the primary traversal. pub struct PreTraverseToken { traverse: bool, - skip_root: bool, + unstyled_children_only: bool, } impl PreTraverseToken { @@ -112,8 +112,8 @@ impl PreTraverseToken { self.traverse } - pub fn should_skip_root(&self) -> bool { - self.skip_root + pub fn traverse_unstyled_children_only(&self) -> bool { + self.unstyled_children_only } } @@ -140,16 +140,16 @@ pub trait DomTraversalContext { /// a traversal is needed. Returns a token that allows the caller to prove /// that the call happened. /// - /// The skip_root parameter is used in Gecko to style newly-appended children - /// without restyling the parent. - fn pre_traverse(root: N::ConcreteElement, stylist: &Stylist, skip_root: bool) + /// The unstyled_children_only parameter is used in Gecko to style newly- + /// appended children without restyling the parent. + fn pre_traverse(root: N::ConcreteElement, stylist: &Stylist, + unstyled_children_only: bool) -> PreTraverseToken { - // If we should skip the root, traverse unconditionally. - if skip_root { + if unstyled_children_only { return PreTraverseToken { traverse: true, - skip_root: true, + unstyled_children_only: true, }; } @@ -168,7 +168,7 @@ pub trait DomTraversalContext { PreTraverseToken { traverse: Self::node_needs_traversal(root.as_node()), - skip_root: false, + unstyled_children_only: false, } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 3e6bcdbf446..8d832d393a9 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -118,7 +118,7 @@ fn create_shared_context(mut per_doc_data: &mut AtomicRefMut () { + behavior: structs::TraversalRootBehavior) -> () { let element = GeckoElement(root); debug!("Servo_TraverseSubtree: {:?}", element); - traverse_subtree(element, raw_data, skip_root == structs::SkipRootBehavior::Skip); + traverse_subtree(element, raw_data, + behavior == structs::TraversalRootBehavior::UnstyledChildrenOnly); } #[no_mangle] From 61eadbe7f113a620464444298b4902d859fe6aef Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 12 Dec 2016 15:36:41 -0800 Subject: [PATCH 4/4] Bug 1322945 - Improve ergonomics and share more code for style crate DOM tree logging. r=heycam MozReview-Commit-ID: 4Fy3ujpI4n2 --- components/layout_thread/lib.rs | 8 +-- components/script/layout_wrapper.rs | 63 ++------------------ components/style/dom.rs | 91 +++++++++++++++++++++++++++-- components/style/gecko/wrapper.rs | 29 ++++----- ports/geckolib/glue.rs | 5 +- 5 files changed, 113 insertions(+), 83 deletions(-) diff --git a/components/layout_thread/lib.rs b/components/layout_thread/lib.rs index 9994eb54bdc..ea834095e6e 100644 --- a/components/layout_thread/lib.rs +++ b/components/layout_thread/lib.rs @@ -106,7 +106,7 @@ use std::sync::mpsc::{Receiver, Sender, channel}; use style::animation::Animation; use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext}; use style::data::StoredRestyleHint; -use style::dom::{TElement, TNode}; +use style::dom::{ShowSubtree, ShowSubtreeDataAndPrimaryValues, TElement, TNode}; use style::error_reporting::{ParseErrorReporter, StdoutErrorReporter}; use style::logical_geometry::LogicalPoint; use style::media_queries::{Device, MediaType}; @@ -1035,9 +1035,7 @@ impl LayoutThread { debug!("layout: processing reflow request for: {:?} ({}) (query={:?})", element, self.url, data.query_type); - if log_enabled!(log::LogLevel::Debug) { - element.as_node().dump(); - } + debug!("{:?}", ShowSubtree(element.as_node())); let initial_viewport = data.window_size.initial_viewport; let old_viewport_size = self.viewport_size; @@ -1181,7 +1179,7 @@ impl LayoutThread { } if opts::get().dump_style_tree { - element.as_node().dump_style(); + println!("{:?}", ShowSubtreeDataAndPrimaryValues(element.as_node())); } if opts::get().dump_rule_tree { diff --git a/components/script/layout_wrapper.rs b/components/script/layout_wrapper.rs index ab69e8f2cab..0fa54e6fe75 100644 --- a/components/script/layout_wrapper.rs +++ b/components/script/layout_wrapper.rs @@ -86,7 +86,11 @@ impl<'ln> Debug for ServoLayoutNode<'ln> { if let Some(el) = self.as_element() { el.fmt(f) } else { - write!(f, "{:?} ({:#x})", self.type_id(), self.opaque().0) + if self.is_text_node() { + write!(f, " ({:#x})", self.opaque().0) + } else { + write!(f, " ({:#x})", self.opaque().0) + } } } } @@ -156,15 +160,6 @@ impl<'ln> TNode for ServoLayoutNode<'ln> { transmute(node) } - fn dump(self) { - self.dump_indent(0); - } - - fn dump_style(self) { - println!("\nDOM with computed styles:"); - self.dump_style_indent(0); - } - fn children(self) -> LayoutIterator> { LayoutIterator(ServoChildrenIterator { current: self.first_child(), @@ -290,54 +285,6 @@ impl<'le> GetLayoutData for ServoThreadSafeLayoutElement<'le> { } impl<'ln> ServoLayoutNode<'ln> { - fn dump_indent(self, indent: u32) { - let mut s = String::new(); - for _ in 0..indent { - s.push_str(" "); - } - - s.push_str(&self.debug_str()); - println!("{}", s); - - for kid in self.children() { - kid.dump_indent(indent + 1); - } - } - - fn dump_style_indent(self, indent: u32) { - if self.is_element() { - let mut s = String::new(); - for _ in 0..indent { - s.push_str(" "); - } - s.push_str(&self.debug_style_str()); - println!("{}", s); - } - - for kid in self.children() { - kid.dump_style_indent(indent + 1); - } - } - - fn debug_str(self) -> String { - format!("{:?}: dirty_descendants={}", - self.script_type_id(), - self.as_element().map_or(false, |el| el.has_dirty_descendants())) - } - - fn debug_style_str(self) -> String { - let maybe_element = self.as_element(); - let maybe_data = match maybe_element { - Some(ref el) => el.borrow_data(), - None => None, - }; - if let Some(data) = maybe_data { - format!("{:?}: {:?}", self.script_type_id(), &*data) - } else { - format!("{:?}: style_data=None", self.script_type_id()) - } - } - /// Returns the interior of this node as a `LayoutJS`. This is highly unsafe for layout to /// call and as such is marked `unsafe`. pub unsafe fn get_jsmanaged(&self) -> &LayoutJS { diff --git a/components/style/dom.rs b/components/style/dom.rs index f32f5e8361e..75069ce9305 100644 --- a/components/style/dom.rs +++ b/components/style/dom.rs @@ -14,6 +14,7 @@ use parking_lot::RwLock; use properties::{ComputedValues, PropertyDeclarationBlock}; use selector_parser::{ElementExt, PreExistingComputedValues, PseudoElement}; use sink::Push; +use std::fmt; use std::fmt::Debug; use std::sync::Arc; use stylist::ApplicableDeclarationBlock; @@ -67,17 +68,13 @@ impl Iterator for LayoutIterator where T: Iterator, I: NodeInfo } } -pub trait TNode : Sized + Copy + Clone + NodeInfo { +pub trait TNode : Sized + Copy + Clone + Debug + NodeInfo { type ConcreteElement: TElement; type ConcreteChildrenIterator: Iterator; fn to_unsafe(&self) -> UnsafeNode; unsafe fn from_unsafe(n: &UnsafeNode) -> Self; - fn dump(self); - - fn dump_style(self); - /// Returns an iterator over this node's children. fn children(self) -> LayoutIterator; @@ -103,6 +100,90 @@ pub trait TNode : Sized + Copy + Clone + NodeInfo { fn parent_node(&self) -> Option; } +/// Wrapper to output the ElementData along with the node when formatting for +/// Debug. +pub struct ShowData(pub N); +impl Debug for ShowData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt_with_data(f, self.0) + } +} + +/// Wrapper to output the primary computed values along with the node when +/// formatting for Debug. This is very verbose. +pub struct ShowDataAndPrimaryValues(pub N); +impl Debug for ShowDataAndPrimaryValues { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt_with_data_and_primary_values(f, self.0) + } +} + +/// Wrapper to output the subtree rather than the single node when formatting +/// for Debug. +pub struct ShowSubtree(pub N); +impl Debug for ShowSubtree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(writeln!(f, "DOM Subtree:")); + fmt_subtree(f, &|f, n| write!(f, "{:?}", n), self.0, 1) + } +} + +/// Wrapper to output the subtree along with the ElementData when formatting +/// for Debug. +pub struct ShowSubtreeData(pub N); +impl Debug for ShowSubtreeData { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(writeln!(f, "DOM Subtree:")); + fmt_subtree(f, &|f, n| fmt_with_data(f, n), self.0, 1) + } +} + +/// Wrapper to output the subtree along with the ElementData and primary +/// ComputedValues when formatting for Debug. This is extremely verbose. +pub struct ShowSubtreeDataAndPrimaryValues(pub N); +impl Debug for ShowSubtreeDataAndPrimaryValues { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + try!(writeln!(f, "DOM Subtree:")); + fmt_subtree(f, &|f, n| fmt_with_data_and_primary_values(f, n), self.0, 1) + } +} + +fn fmt_with_data(f: &mut fmt::Formatter, n: N) -> fmt::Result { + if let Some(el) = n.as_element() { + write!(f, "{:?} dd={} data={:?}", el, el.has_dirty_descendants(), el.borrow_data()) + } else { + write!(f, "{:?}", n) + } +} + +fn fmt_with_data_and_primary_values(f: &mut fmt::Formatter, n: N) -> fmt::Result { + if let Some(el) = n.as_element() { + let dd = el.has_dirty_descendants(); + let data = el.borrow_data(); + let styles = data.as_ref().and_then(|d| d.get_styles()); + let values = styles.map(|s| &s.primary.values); + write!(f, "{:?} dd={} data={:?} values={:?}", el, dd, &data, values) + } else { + write!(f, "{:?}", n) + } +} + +fn fmt_subtree(f: &mut fmt::Formatter, stringify: &F, n: N, indent: u32) + -> fmt::Result + where F: Fn(&mut fmt::Formatter, N) -> fmt::Result +{ + for _ in 0..indent { + try!(write!(f, " ")); + } + try!(stringify(f, n)); + for kid in n.children() { + try!(writeln!(f, "")); + try!(fmt_subtree(f, stringify, kid, indent + 1)); + } + + Ok(()) +} + pub trait PresentationalHintsSynthetizer { fn synthesize_presentational_hints_for_legacy_attributes(&self, hints: &mut V) where V: Push; diff --git a/components/style/gecko/wrapper.rs b/components/style/gecko/wrapper.rs index f8629a24fe3..4f9ef32305a 100644 --- a/components/style/gecko/wrapper.rs +++ b/components/style/gecko/wrapper.rs @@ -50,6 +50,20 @@ use stylist::ApplicableDeclarationBlock; #[derive(Clone, Copy)] pub struct GeckoNode<'ln>(pub &'ln RawGeckoNode); +impl<'ln> fmt::Debug for GeckoNode<'ln> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(el) = self.as_element() { + el.fmt(f) + } else { + if self.is_text_node() { + write!(f, " ({:#x})", self.opaque().0) + } else { + write!(f, " ({:#x})", self.opaque().0) + } + } + } +} + impl<'ln> GeckoNode<'ln> { fn from_content(content: &'ln nsIContent) -> Self { GeckoNode(&content._base) @@ -102,19 +116,6 @@ impl<'ln> TNode for GeckoNode<'ln> { GeckoNode(&*(n.0 as *mut RawGeckoNode)) } - fn dump(self) { - if self.is_text_node() { - println!("Text ({:?})", &self.0 as *const _); - } else { - let el = self.as_element().unwrap(); - println!("Element {} ({:?})", el.get_local_name(), &el.0 as *const _); - } - } - - fn dump_style(self) { - unimplemented!() - } - fn children(self) -> LayoutIterator> { let maybe_iter = unsafe { Gecko_MaybeCreateStyleChildrenIterator(self.0) }; if let Some(iter) = maybe_iter.into_owned_opt() { @@ -211,7 +212,7 @@ impl<'le> fmt::Debug for GeckoElement<'le> { if let Some(id) = self.get_id() { try!(write!(f, " id={}", id)); } - write!(f, "> ({:?})", self.0 as *const _) + write!(f, "> ({:#x})", self.as_node().opaque().0) } } diff --git a/ports/geckolib/glue.rs b/ports/geckolib/glue.rs index 8d832d393a9..63016b1a50a 100644 --- a/ports/geckolib/glue.rs +++ b/ports/geckolib/glue.rs @@ -18,7 +18,7 @@ use style::arc_ptr_eq; use style::atomic_refcell::AtomicRefMut; use style::context::{LocalStyleContextCreationInfo, ReflowGoal, SharedStyleContext}; use style::data::{ElementData, RestyleData}; -use style::dom::{TElement, TNode}; +use style::dom::{ShowSubtreeData, TElement, TNode}; use style::error_reporting::StdoutErrorReporter; use style::gecko::context::StandaloneStyleContext; use style::gecko::context::clear_local_context; @@ -145,6 +145,9 @@ fn traverse_subtree(element: GeckoElement, raw_data: RawServoStyleSetBorrowed, return; } + debug!("Traversing subtree:"); + debug!("{:?}", ShowSubtreeData(element.as_node())); + let shared_style_context = create_shared_context(&mut per_doc_data); let known_depth = None;