mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
Auto merge of #26484 - servo:layout-2020-style-prep, r=SimonSapin
Implement concept of dirty root
This commit is contained in:
commit
79b6758cb9
16 changed files with 294 additions and 97 deletions
|
@ -30,6 +30,10 @@ impl<'a> RecalcStyleAndConstructFlows<'a> {
|
||||||
RecalcStyleAndConstructFlows { context: context }
|
RecalcStyleAndConstructFlows { context: context }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn context(&self) -> &LayoutContext<'a> {
|
||||||
|
&self.context
|
||||||
|
}
|
||||||
|
|
||||||
/// Consumes this traversal context, returning ownership of the shared layout
|
/// Consumes this traversal context, returning ownership of the shared layout
|
||||||
/// context to the caller.
|
/// context to the caller.
|
||||||
pub fn destroy(self) -> LayoutContext<'a> {
|
pub fn destroy(self) -> LayoutContext<'a> {
|
||||||
|
@ -183,6 +187,19 @@ where
|
||||||
fn process(&mut self, node: &ConcreteThreadSafeLayoutNode);
|
fn process(&mut self, node: &ConcreteThreadSafeLayoutNode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
#[inline]
|
||||||
|
pub unsafe fn construct_flows_at_ancestors<'dom>(
|
||||||
|
context: &LayoutContext,
|
||||||
|
mut node: impl LayoutNode<'dom>,
|
||||||
|
) {
|
||||||
|
while let Some(element) = node.traversal_parent() {
|
||||||
|
element.set_dirty_descendants();
|
||||||
|
node = element.as_node();
|
||||||
|
construct_flows_at(context, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The flow construction traversal, which builds flows for styled nodes.
|
/// The flow construction traversal, which builds flows for styled nodes.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
|
|
@ -34,7 +34,7 @@ use crate::data::{LayoutData, LayoutDataFlags, StyleAndLayoutData};
|
||||||
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
use atomic_refcell::{AtomicRef, AtomicRefMut};
|
||||||
use script_layout_interface::wrapper_traits::GetStyleAndOpaqueLayoutData;
|
use script_layout_interface::wrapper_traits::GetStyleAndOpaqueLayoutData;
|
||||||
use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
use script_layout_interface::wrapper_traits::{ThreadSafeLayoutElement, ThreadSafeLayoutNode};
|
||||||
use style::dom::{NodeInfo, TNode};
|
use style::dom::{NodeInfo, TElement, TNode};
|
||||||
use style::selector_parser::RestyleDamage;
|
use style::selector_parser::RestyleDamage;
|
||||||
use style::values::computed::counters::ContentItem;
|
use style::values::computed::counters::ContentItem;
|
||||||
use style::values::generics::counters::Content;
|
use style::values::generics::counters::Content;
|
||||||
|
@ -148,7 +148,13 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
let damage = {
|
let damage = {
|
||||||
let data = node.get_style_and_layout_data().unwrap();
|
let data = match node.get_style_and_layout_data() {
|
||||||
|
Some(data) => data,
|
||||||
|
None => panic!(
|
||||||
|
"could not get style and layout data for <{}>",
|
||||||
|
node.as_element().unwrap().local_name()
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
if !data
|
if !data
|
||||||
.layout_data
|
.layout_data
|
||||||
|
|
|
@ -346,8 +346,10 @@ impl BoxSlot<'_> {
|
||||||
|
|
||||||
impl Drop for BoxSlot<'_> {
|
impl Drop for BoxSlot<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let Some(slot) = &mut self.slot {
|
if !std::thread::panicking() {
|
||||||
assert!(slot.borrow().is_some(), "failed to set a layout box");
|
if let Some(slot) = &mut self.slot {
|
||||||
|
assert!(slot.borrow().is_some(), "failed to set a layout box");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,7 +57,8 @@ use layout::query::{process_node_scroll_area_request, process_node_scroll_id_req
|
||||||
use layout::query::{process_offset_parent_query, process_resolved_style_request};
|
use layout::query::{process_offset_parent_query, process_resolved_style_request};
|
||||||
use layout::sequential;
|
use layout::sequential;
|
||||||
use layout::traversal::{
|
use layout::traversal::{
|
||||||
ComputeStackingRelativePositions, PreorderFlowTraversal, RecalcStyleAndConstructFlows,
|
construct_flows_at_ancestors, ComputeStackingRelativePositions, PreorderFlowTraversal,
|
||||||
|
RecalcStyleAndConstructFlows,
|
||||||
};
|
};
|
||||||
use layout::wrapper::LayoutNodeLayoutData;
|
use layout::wrapper::LayoutNodeLayoutData;
|
||||||
use layout_traits::LayoutThreadFactory;
|
use layout_traits::LayoutThreadFactory;
|
||||||
|
@ -1200,7 +1201,7 @@ impl LayoutThread {
|
||||||
.expect("layout: wrong layout query timestamp");
|
.expect("layout: wrong layout query timestamp");
|
||||||
};
|
};
|
||||||
|
|
||||||
let element = match document.root_element() {
|
let root_element = match document.root_element() {
|
||||||
None => {
|
None => {
|
||||||
// Since we cannot compute anything, give spec-required placeholders.
|
// Since we cannot compute anything, give spec-required placeholders.
|
||||||
debug!("layout: No root node: bailing");
|
debug!("layout: No root node: bailing");
|
||||||
|
@ -1250,9 +1251,9 @@ impl LayoutThread {
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"layout: processing reflow request for: {:?} ({}) (query={:?})",
|
"layout: processing reflow request for: {:?} ({}) (query={:?})",
|
||||||
element, self.url, data.reflow_goal
|
root_element, self.url, data.reflow_goal
|
||||||
);
|
);
|
||||||
trace!("{:?}", ShowSubtree(element.as_node()));
|
trace!("{:?}", ShowSubtree(root_element.as_node()));
|
||||||
|
|
||||||
let initial_viewport = data.window_size.initial_viewport;
|
let initial_viewport = data.window_size.initial_viewport;
|
||||||
let device_pixel_ratio = data.window_size.device_pixel_ratio;
|
let device_pixel_ratio = data.window_size.device_pixel_ratio;
|
||||||
|
@ -1309,7 +1310,7 @@ impl LayoutThread {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
if had_used_viewport_units {
|
if had_used_viewport_units {
|
||||||
if let Some(mut data) = element.mutate_data() {
|
if let Some(mut data) = root_element.mutate_data() {
|
||||||
data.hint.insert(RestyleHint::recascade_subtree());
|
data.hint.insert(RestyleHint::recascade_subtree());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1344,7 +1345,7 @@ impl LayoutThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
if viewport_size_changed {
|
if viewport_size_changed {
|
||||||
if let Some(mut flow) = self.try_get_layout_root(element.as_node()) {
|
if let Some(mut flow) = self.try_get_layout_root(root_element.as_node()) {
|
||||||
LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
|
LayoutThread::reflow_all_nodes(FlowRef::deref_mut(&mut flow));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1395,7 +1396,7 @@ impl LayoutThread {
|
||||||
debug!("Noting restyle for {:?}: {:?}", el, style_data);
|
debug!("Noting restyle for {:?}: {:?}", el, style_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stylist.flush(&guards, Some(element), Some(&map));
|
self.stylist.flush(&guards, Some(root_element), Some(&map));
|
||||||
|
|
||||||
// Create a layout context for use throughout the following passes.
|
// Create a layout context for use throughout the following passes.
|
||||||
let mut layout_context = self.build_layout_context(
|
let mut layout_context = self.build_layout_context(
|
||||||
|
@ -1414,13 +1415,19 @@ impl LayoutThread {
|
||||||
(None, 1)
|
(None, 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let dirty_root = unsafe {
|
||||||
|
ServoLayoutNode::new(&data.dirty_root.unwrap())
|
||||||
|
.as_element()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let traversal = RecalcStyleAndConstructFlows::new(layout_context);
|
let traversal = RecalcStyleAndConstructFlows::new(layout_context);
|
||||||
let token = {
|
let token = {
|
||||||
let shared =
|
let shared =
|
||||||
<RecalcStyleAndConstructFlows as DomTraversal<ServoLayoutElement>>::shared_context(
|
<RecalcStyleAndConstructFlows as DomTraversal<ServoLayoutElement>>::shared_context(
|
||||||
&traversal,
|
&traversal,
|
||||||
);
|
);
|
||||||
RecalcStyleAndConstructFlows::pre_traverse(element, shared)
|
RecalcStyleAndConstructFlows::pre_traverse(dirty_root, shared)
|
||||||
};
|
};
|
||||||
|
|
||||||
if token.should_traverse() {
|
if token.should_traverse() {
|
||||||
|
@ -1431,11 +1438,13 @@ impl LayoutThread {
|
||||||
self.time_profiler_chan.clone(),
|
self.time_profiler_chan.clone(),
|
||||||
|| {
|
|| {
|
||||||
// Perform CSS selector matching and flow construction.
|
// Perform CSS selector matching and flow construction.
|
||||||
driver::traverse_dom::<ServoLayoutElement, RecalcStyleAndConstructFlows>(
|
let root = driver::traverse_dom::<
|
||||||
&traversal,
|
ServoLayoutElement,
|
||||||
token,
|
RecalcStyleAndConstructFlows,
|
||||||
thread_pool,
|
>(&traversal, token, thread_pool);
|
||||||
);
|
unsafe {
|
||||||
|
construct_flows_at_ancestors(traversal.context(), root.as_node());
|
||||||
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
// TODO(pcwalton): Measure energy usage of text shaping, perhaps?
|
||||||
|
@ -1452,7 +1461,7 @@ impl LayoutThread {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Retrieve the (possibly rebuilt) root flow.
|
// Retrieve the (possibly rebuilt) root flow.
|
||||||
*self.root_flow.borrow_mut() = self.try_get_layout_root(element.as_node());
|
*self.root_flow.borrow_mut() = self.try_get_layout_root(root_element.as_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
for element in elements_with_snapshot {
|
for element in elements_with_snapshot {
|
||||||
|
@ -1462,7 +1471,10 @@ impl LayoutThread {
|
||||||
layout_context = traversal.destroy();
|
layout_context = traversal.destroy();
|
||||||
|
|
||||||
if self.dump_style_tree {
|
if self.dump_style_tree {
|
||||||
println!("{:?}", ShowSubtreeDataAndPrimaryValues(element.as_node()));
|
println!(
|
||||||
|
"{:?}",
|
||||||
|
ShowSubtreeDataAndPrimaryValues(root_element.as_node())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.dump_rule_tree {
|
if self.dump_rule_tree {
|
||||||
|
|
|
@ -1307,7 +1307,10 @@ impl<'le> ThreadSafeLayoutElement<'le> for ServoThreadSafeLayoutElement<'le> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn style_data(&self) -> AtomicRef<ElementData> {
|
fn style_data(&self) -> AtomicRef<ElementData> {
|
||||||
self.element.borrow_data().expect("Unstyled layout node?")
|
match self.element.borrow_data() {
|
||||||
|
Some(data) => data,
|
||||||
|
None => panic!("could not find styles for <{}>", self.element.local_name()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_shadow_host(&self) -> bool {
|
fn is_shadow_host(&self) -> bool {
|
||||||
|
|
|
@ -89,7 +89,6 @@ use style::dom::{TDocument, TElement, TNode};
|
||||||
use style::driver;
|
use style::driver;
|
||||||
use style::error_reporting::RustLogReporter;
|
use style::error_reporting::RustLogReporter;
|
||||||
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
|
use style::global_style_data::{GLOBAL_STYLE_DATA, STYLE_THREAD_POOL};
|
||||||
use style::invalidation::element::restyle_hints::RestyleHint;
|
|
||||||
use style::media_queries::{Device, MediaList, MediaType};
|
use style::media_queries::{Device, MediaList, MediaType};
|
||||||
use style::properties::PropertyId;
|
use style::properties::PropertyId;
|
||||||
use style::selector_parser::SnapshotMap;
|
use style::selector_parser::SnapshotMap;
|
||||||
|
@ -891,7 +890,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
let mut rw_data = possibly_locked_rw_data.lock();
|
let mut rw_data = possibly_locked_rw_data.lock();
|
||||||
|
|
||||||
let element = match document.root_element() {
|
let root_element = match document.root_element() {
|
||||||
None => {
|
None => {
|
||||||
// Since we cannot compute anything, give spec-required placeholders.
|
// Since we cannot compute anything, give spec-required placeholders.
|
||||||
debug!("layout: No root node: bailing");
|
debug!("layout: No root node: bailing");
|
||||||
|
@ -962,7 +961,6 @@ impl LayoutThread {
|
||||||
ua_or_user: &ua_or_user_guard,
|
ua_or_user: &ua_or_user_guard,
|
||||||
};
|
};
|
||||||
|
|
||||||
let had_used_viewport_units = self.stylist.device().used_viewport_units();
|
|
||||||
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
|
let device = Device::new(MediaType::screen(), initial_viewport, device_pixel_ratio);
|
||||||
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
|
let sheet_origins_affected_by_device_change = self.stylist.set_device(device, &guards);
|
||||||
|
|
||||||
|
@ -990,11 +988,6 @@ impl LayoutThread {
|
||||||
))
|
))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
if had_used_viewport_units {
|
|
||||||
if let Some(mut data) = element.mutate_data() {
|
|
||||||
data.hint.insert(RestyleHint::recascade_subtree());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.first_reflow.get() {
|
if self.first_reflow.get() {
|
||||||
|
@ -1062,7 +1055,7 @@ impl LayoutThread {
|
||||||
debug!("Noting restyle for {:?}: {:?}", el, style_data);
|
debug!("Noting restyle for {:?}: {:?}", el, style_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.stylist.flush(&guards, Some(element), Some(&map));
|
self.stylist.flush(&guards, Some(root_element), Some(&map));
|
||||||
|
|
||||||
// Create a layout context for use throughout the following passes.
|
// Create a layout context for use throughout the following passes.
|
||||||
let mut layout_context = self.build_layout_context(
|
let mut layout_context = self.build_layout_context(
|
||||||
|
@ -1073,10 +1066,16 @@ impl LayoutThread {
|
||||||
data.animations.clone(),
|
data.animations.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let dirty_root = unsafe {
|
||||||
|
ServoLayoutNode::new(&data.dirty_root.unwrap())
|
||||||
|
.as_element()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
let traversal = RecalcStyle::new(layout_context);
|
let traversal = RecalcStyle::new(layout_context);
|
||||||
let token = {
|
let token = {
|
||||||
let shared = DomTraversal::<ServoLayoutElement>::shared_context(&traversal);
|
let shared = DomTraversal::<ServoLayoutElement>::shared_context(&traversal);
|
||||||
RecalcStyle::pre_traverse(element, shared)
|
RecalcStyle::pre_traverse(dirty_root, shared)
|
||||||
};
|
};
|
||||||
|
|
||||||
let rayon_pool = STYLE_THREAD_POOL.pool();
|
let rayon_pool = STYLE_THREAD_POOL.pool();
|
||||||
|
@ -1085,7 +1084,7 @@ impl LayoutThread {
|
||||||
let box_tree = if token.should_traverse() {
|
let box_tree = if token.should_traverse() {
|
||||||
driver::traverse_dom(&traversal, token, rayon_pool);
|
driver::traverse_dom(&traversal, token, rayon_pool);
|
||||||
|
|
||||||
let root_node = document.root_element().unwrap().as_node();
|
let root_node = root_element.as_node();
|
||||||
let build_box_tree = || BoxTree::construct(traversal.context(), root_node);
|
let build_box_tree = || BoxTree::construct(traversal.context(), root_node);
|
||||||
let box_tree = if let Some(pool) = rayon_pool {
|
let box_tree = if let Some(pool) = rayon_pool {
|
||||||
pool.install(build_box_tree)
|
pool.install(build_box_tree)
|
||||||
|
@ -1122,7 +1121,7 @@ impl LayoutThread {
|
||||||
if self.dump_style_tree {
|
if self.dump_style_tree {
|
||||||
println!(
|
println!(
|
||||||
"{:?}",
|
"{:?}",
|
||||||
style::dom::ShowSubtreeDataAndPrimaryValues(element.as_node())
|
style::dom::ShowSubtreeDataAndPrimaryValues(root_element.as_node())
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -45,11 +45,12 @@ impl<T> DomRefCell<T> {
|
||||||
&mut *self.value.as_ptr()
|
&mut *self.value.as_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Version of the above that we use during restyle while the script thread
|
/// Mutably borrow a cell for layout. Ideally this would use
|
||||||
/// is blocked.
|
/// `RefCell::try_borrow_mut_unguarded` but that doesn't exist yet.
|
||||||
pub fn borrow_mut_for_layout(&self) -> RefMut<T> {
|
#[allow(unsafe_code)]
|
||||||
|
pub unsafe fn borrow_mut_for_layout(&self) -> &mut T {
|
||||||
debug_assert!(thread_state::get().is_layout());
|
debug_assert!(thread_state::get().is_layout());
|
||||||
self.value.borrow_mut()
|
&mut *self.value.as_ptr()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -387,6 +387,8 @@ pub struct Document {
|
||||||
animation_timeline: DomRefCell<AnimationTimeline>,
|
animation_timeline: DomRefCell<AnimationTimeline>,
|
||||||
/// Animations for this Document
|
/// Animations for this Document
|
||||||
animations: DomRefCell<Animations>,
|
animations: DomRefCell<Animations>,
|
||||||
|
/// The nearest inclusive ancestors to all the nodes that require a restyle.
|
||||||
|
dirty_root: MutNullableDom<Element>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
@ -446,6 +448,112 @@ enum ElementLookupResult {
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
impl Document {
|
impl Document {
|
||||||
|
pub fn note_node_with_dirty_descendants(&self, node: &Node) {
|
||||||
|
debug_assert!(*node.owner_doc() == *self);
|
||||||
|
if !node.is_connected() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let parent = match node.inclusive_ancestors(ShadowIncluding::Yes).nth(1) {
|
||||||
|
Some(parent) => parent,
|
||||||
|
None => {
|
||||||
|
// There is no parent so this is the Document node, so we
|
||||||
|
// behave as if we were called with the document element.
|
||||||
|
let document_element = match self.GetDocumentElement() {
|
||||||
|
Some(element) => element,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
if let Some(dirty_root) = self.dirty_root.get() {
|
||||||
|
// There was an existing dirty root so we mark its
|
||||||
|
// ancestors as dirty until the document element.
|
||||||
|
for ancestor in dirty_root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.inclusive_ancestors(ShadowIncluding::Yes)
|
||||||
|
{
|
||||||
|
if ancestor.is::<Element>() {
|
||||||
|
ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.dirty_root.set(Some(&document_element));
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
if parent.is::<Element>() {
|
||||||
|
if !parent.is_styled() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if parent.is_display_none() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let element_parent: DomRoot<Element>;
|
||||||
|
let element = match node.downcast::<Element>() {
|
||||||
|
Some(element) => element,
|
||||||
|
None => {
|
||||||
|
// Current node is not an element, it's probably a text node,
|
||||||
|
// we try to get its element parent.
|
||||||
|
match DomRoot::downcast::<Element>(parent) {
|
||||||
|
Some(parent) => {
|
||||||
|
element_parent = parent;
|
||||||
|
&element_parent
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Parent is not an element so it must be a document,
|
||||||
|
// and this is not an element either, so there is
|
||||||
|
// nothing to do.
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
let dirty_root = match self.dirty_root.get() {
|
||||||
|
None => {
|
||||||
|
element
|
||||||
|
.upcast::<Node>()
|
||||||
|
.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
|
||||||
|
self.dirty_root.set(Some(element));
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
Some(root) => root,
|
||||||
|
};
|
||||||
|
|
||||||
|
for ancestor in element
|
||||||
|
.upcast::<Node>()
|
||||||
|
.inclusive_ancestors(ShadowIncluding::Yes)
|
||||||
|
{
|
||||||
|
if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ancestor.is::<Element>() {
|
||||||
|
ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_dirty_root = element
|
||||||
|
.upcast::<Node>()
|
||||||
|
.common_ancestor(dirty_root.upcast(), ShadowIncluding::Yes);
|
||||||
|
|
||||||
|
let mut has_dirty_descendants = true;
|
||||||
|
for ancestor in dirty_root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.inclusive_ancestors(ShadowIncluding::Yes)
|
||||||
|
{
|
||||||
|
ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, has_dirty_descendants);
|
||||||
|
has_dirty_descendants &= *ancestor != *new_dirty_root;
|
||||||
|
}
|
||||||
|
self.dirty_root
|
||||||
|
.set(Some(new_dirty_root.downcast::<Element>().unwrap()));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn take_dirty_root(&self) -> Option<DomRoot<Element>> {
|
||||||
|
self.dirty_root.take()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn loader(&self) -> Ref<DocumentLoader> {
|
pub fn loader(&self) -> Ref<DocumentLoader> {
|
||||||
self.loader.borrow()
|
self.loader.borrow()
|
||||||
|
@ -967,8 +1075,14 @@ impl Document {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dirty_all_nodes(&self) {
|
pub fn dirty_all_nodes(&self) {
|
||||||
let root = self.upcast::<Node>();
|
let root = match self.GetDocumentElement() {
|
||||||
for node in root.traverse_preorder(ShadowIncluding::Yes) {
|
Some(root) => root,
|
||||||
|
None => return,
|
||||||
|
};
|
||||||
|
for node in root
|
||||||
|
.upcast::<Node>()
|
||||||
|
.traverse_preorder(ShadowIncluding::Yes)
|
||||||
|
{
|
||||||
node.dirty(NodeDamage::OtherNodeDamage)
|
node.dirty(NodeDamage::OtherNodeDamage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2917,6 +3031,7 @@ impl Document {
|
||||||
DomRefCell::new(AnimationTimeline::new())
|
DomRefCell::new(AnimationTimeline::new())
|
||||||
},
|
},
|
||||||
animations: DomRefCell::new(Animations::new()),
|
animations: DomRefCell::new(Animations::new()),
|
||||||
|
dirty_root: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,7 @@ impl Element {
|
||||||
restyle.hint.insert(RestyleHint::RESTYLE_SELF);
|
restyle.hint.insert(RestyleHint::RESTYLE_SELF);
|
||||||
|
|
||||||
if damage == NodeDamage::OtherNodeDamage {
|
if damage == NodeDamage::OtherNodeDamage {
|
||||||
|
doc.note_node_with_dirty_descendants(self.upcast());
|
||||||
restyle.damage = RestyleDamage::rebuild_and_reflow();
|
restyle.damage = RestyleDamage::rebuild_and_reflow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,6 +516,7 @@ impl Element {
|
||||||
|
|
||||||
pub fn detach_shadow(&self) {
|
pub fn detach_shadow(&self) {
|
||||||
if let Some(ref shadow_root) = self.shadow_root() {
|
if let Some(ref shadow_root) = self.shadow_root() {
|
||||||
|
self.upcast::<Node>().note_dirty_descendants();
|
||||||
shadow_root.detach();
|
shadow_root.detach();
|
||||||
self.ensure_rare_data().shadow_root = None;
|
self.ensure_rare_data().shadow_root = None;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -150,12 +150,9 @@ pub struct Node {
|
||||||
/// are this node.
|
/// are this node.
|
||||||
ranges: WeakRangeVec,
|
ranges: WeakRangeVec,
|
||||||
|
|
||||||
/// Style+Layout information. Only the layout thread may touch this data.
|
/// Style+Layout information.
|
||||||
///
|
#[ignore_malloc_size_of = "trait object"]
|
||||||
/// Must be sent back to the layout thread to be destroyed when this
|
style_and_layout_data: DomRefCell<Option<Box<StyleAndOpaqueLayoutData>>>,
|
||||||
/// node is finalized.
|
|
||||||
#[ignore_malloc_size_of = "Unsafe cell"]
|
|
||||||
style_and_layout_data: UnsafeCell<Option<Box<StyleAndOpaqueLayoutData>>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
@ -318,6 +315,8 @@ impl Node {
|
||||||
/// Fails unless `child` is a child of this node.
|
/// Fails unless `child` is a child of this node.
|
||||||
fn remove_child(&self, child: &Node, cached_index: Option<u32>) {
|
fn remove_child(&self, child: &Node, cached_index: Option<u32>) {
|
||||||
assert!(child.parent_node.get().as_deref() == Some(self));
|
assert!(child.parent_node.get().as_deref() == Some(self));
|
||||||
|
self.note_dirty_descendants();
|
||||||
|
|
||||||
let prev_sibling = child.GetPreviousSibling();
|
let prev_sibling = child.GetPreviousSibling();
|
||||||
match prev_sibling {
|
match prev_sibling {
|
||||||
None => {
|
None => {
|
||||||
|
@ -630,17 +629,7 @@ impl Node {
|
||||||
|
|
||||||
// FIXME(emilio): This and the function below should move to Element.
|
// FIXME(emilio): This and the function below should move to Element.
|
||||||
pub fn note_dirty_descendants(&self) {
|
pub fn note_dirty_descendants(&self) {
|
||||||
debug_assert!(self.is_connected());
|
self.owner_doc().note_node_with_dirty_descendants(self);
|
||||||
|
|
||||||
for ancestor in self.inclusive_ancestors(ShadowIncluding::Yes) {
|
|
||||||
if ancestor.get_flag(NodeFlags::HAS_DIRTY_DESCENDANTS) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ancestor.is::<Element>() {
|
|
||||||
ancestor.set_flag(NodeFlags::HAS_DIRTY_DESCENDANTS, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_dirty_descendants(&self) -> bool {
|
pub fn has_dirty_descendants(&self) -> bool {
|
||||||
|
@ -708,6 +697,22 @@ impl Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn common_ancestor(
|
||||||
|
&self,
|
||||||
|
other: &Node,
|
||||||
|
shadow_including: ShadowIncluding,
|
||||||
|
) -> DomRoot<Node> {
|
||||||
|
for ancestor in self.inclusive_ancestors(shadow_including) {
|
||||||
|
if other
|
||||||
|
.inclusive_ancestors(shadow_including)
|
||||||
|
.any(|node| node == ancestor)
|
||||||
|
{
|
||||||
|
return ancestor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_inclusive_ancestor_of(&self, parent: &Node) -> bool {
|
pub fn is_inclusive_ancestor_of(&self, parent: &Node) -> bool {
|
||||||
self == parent || self.is_ancestor_of(parent)
|
self == parent || self.is_ancestor_of(parent)
|
||||||
}
|
}
|
||||||
|
@ -1246,21 +1251,38 @@ impl Node {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
pub fn is_styled(&self) -> bool {
|
||||||
pub fn style(&self) -> Option<Arc<ComputedValues>> {
|
self.style_and_layout_data.borrow().is_some()
|
||||||
if !window_from_node(self).layout_reflow(QueryMsg::StyleQuery) {
|
}
|
||||||
return None;
|
|
||||||
}
|
pub fn is_display_none(&self) -> bool {
|
||||||
unsafe {
|
self.style_and_layout_data
|
||||||
(*self.style_and_layout_data.get()).as_ref().map(|data| {
|
.borrow()
|
||||||
|
.as_ref()
|
||||||
|
.map_or(true, |data| {
|
||||||
data.style_data
|
data.style_data
|
||||||
.element_data
|
.element_data
|
||||||
.borrow()
|
.borrow()
|
||||||
.styles
|
.styles
|
||||||
.primary()
|
.primary()
|
||||||
.clone()
|
.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()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1444,13 +1466,21 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> {
|
fn get_style_and_opaque_layout_data(self) -> Option<&'dom StyleAndOpaqueLayoutData> {
|
||||||
unsafe { (*self.unsafe_get().style_and_layout_data.get()).as_deref() }
|
unsafe {
|
||||||
|
self.unsafe_get()
|
||||||
|
.style_and_layout_data
|
||||||
|
.borrow_for_layout()
|
||||||
|
.as_deref()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn init_style_and_opaque_layout_data(self, val: Box<StyleAndOpaqueLayoutData>) {
|
unsafe fn init_style_and_opaque_layout_data(self, val: Box<StyleAndOpaqueLayoutData>) {
|
||||||
let data = &mut *self.unsafe_get().style_and_layout_data.get();
|
let data = self
|
||||||
|
.unsafe_get()
|
||||||
|
.style_and_layout_data
|
||||||
|
.borrow_mut_for_layout();
|
||||||
debug_assert!(data.is_none());
|
debug_assert!(data.is_none());
|
||||||
*data = Some(val);
|
*data = Some(val);
|
||||||
}
|
}
|
||||||
|
@ -1458,7 +1488,9 @@ impl<'dom> LayoutNodeHelpers<'dom> for LayoutDom<'dom, Node> {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe fn take_style_and_opaque_layout_data(self) -> Box<StyleAndOpaqueLayoutData> {
|
unsafe fn take_style_and_opaque_layout_data(self) -> Box<StyleAndOpaqueLayoutData> {
|
||||||
(*self.unsafe_get().style_and_layout_data.get())
|
self.unsafe_get()
|
||||||
|
.style_and_layout_data
|
||||||
|
.borrow_mut_for_layout()
|
||||||
.take()
|
.take()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
@ -1649,7 +1681,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Whether a tree traversal should pass shadow tree boundaries.
|
/// Whether a tree traversal should pass shadow tree boundaries.
|
||||||
#[derive(PartialEq)]
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
pub enum ShadowIncluding {
|
pub enum ShadowIncluding {
|
||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
|
@ -1775,7 +1807,7 @@ impl Node {
|
||||||
inclusive_descendants_version: Cell::new(0),
|
inclusive_descendants_version: Cell::new(0),
|
||||||
ranges: WeakRangeVec::new(),
|
ranges: WeakRangeVec::new(),
|
||||||
|
|
||||||
style_and_layout_data: UnsafeCell::new(None),
|
style_and_layout_data: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -296,19 +296,8 @@ impl RangeMethods for Range {
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer
|
// https://dom.spec.whatwg.org/#dom-range-commonancestorcontainer
|
||||||
fn CommonAncestorContainer(&self) -> DomRoot<Node> {
|
fn CommonAncestorContainer(&self) -> DomRoot<Node> {
|
||||||
let end_container = self.EndContainer();
|
self.EndContainer()
|
||||||
// Step 1.
|
.common_ancestor(&self.StartContainer(), ShadowIncluding::No)
|
||||||
for container in self
|
|
||||||
.StartContainer()
|
|
||||||
.inclusive_ancestors(ShadowIncluding::No)
|
|
||||||
{
|
|
||||||
// Step 2.
|
|
||||||
if container.is_inclusive_ancestor_of(&end_container) {
|
|
||||||
// Step 3.
|
|
||||||
return container;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
unreachable!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-range-setstart
|
// https://dom.spec.whatwg.org/#dom-range-setstart
|
||||||
|
|
|
@ -281,7 +281,7 @@ impl<'dom> LayoutShadowRootHelpers<'dom> for LayoutDom<'dom, ShadowRoot> {
|
||||||
quirks_mode: QuirksMode,
|
quirks_mode: QuirksMode,
|
||||||
guard: &SharedRwLockReadGuard,
|
guard: &SharedRwLockReadGuard,
|
||||||
) {
|
) {
|
||||||
let mut author_styles = (*self.unsafe_get()).author_styles.borrow_mut_for_layout();
|
let author_styles = self.unsafe_get().author_styles.borrow_mut_for_layout();
|
||||||
if author_styles.stylesheets.dirty() {
|
if author_styles.stylesheets.dirty() {
|
||||||
author_styles.flush::<E>(device, quirks_mode, guard);
|
author_styles.flush::<E>(device, quirks_mode, guard);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1659,6 +1659,14 @@ impl Window {
|
||||||
document.flush_dirty_canvases();
|
document.flush_dirty_canvases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pending_restyles = document.drain_pending_restyles();
|
||||||
|
|
||||||
|
let dirty_root = document
|
||||||
|
.take_dirty_root()
|
||||||
|
.filter(|_| !stylesheets_changed)
|
||||||
|
.or_else(|| document.GetDocumentElement())
|
||||||
|
.map(|root| root.upcast::<Node>().to_trusted_node_address());
|
||||||
|
|
||||||
// Send new document and relevant styles to layout.
|
// Send new document and relevant styles to layout.
|
||||||
let needs_display = reflow_goal.needs_display();
|
let needs_display = reflow_goal.needs_display();
|
||||||
let reflow = ScriptReflow {
|
let reflow = ScriptReflow {
|
||||||
|
@ -1666,13 +1674,14 @@ impl Window {
|
||||||
page_clip_rect: self.page_clip_rect.get(),
|
page_clip_rect: self.page_clip_rect.get(),
|
||||||
},
|
},
|
||||||
document: document.upcast::<Node>().to_trusted_node_address(),
|
document: document.upcast::<Node>().to_trusted_node_address(),
|
||||||
|
dirty_root,
|
||||||
stylesheets_changed,
|
stylesheets_changed,
|
||||||
window_size: self.window_size.get(),
|
window_size: self.window_size.get(),
|
||||||
origin: self.origin().immutable().clone(),
|
origin: self.origin().immutable().clone(),
|
||||||
reflow_goal,
|
reflow_goal,
|
||||||
script_join_chan: join_chan,
|
script_join_chan: join_chan,
|
||||||
dom_count: document.dom_count(),
|
dom_count: document.dom_count(),
|
||||||
pending_restyles: document.drain_pending_restyles(),
|
pending_restyles,
|
||||||
animation_timeline_value: document.current_animation_timeline_value(),
|
animation_timeline_value: document.current_animation_timeline_value(),
|
||||||
animations: document.animations().sets.clone(),
|
animations: document.animations().sets.clone(),
|
||||||
};
|
};
|
||||||
|
@ -1770,12 +1779,17 @@ impl Window {
|
||||||
|
|
||||||
// We shouldn't need a reflow immediately after a
|
// We shouldn't need a reflow immediately after a
|
||||||
// reflow, except if we're waiting for a deferred paint.
|
// reflow, except if we're waiting for a deferred paint.
|
||||||
assert!({
|
let condition = self.Document().needs_reflow();
|
||||||
let condition = self.Document().needs_reflow();
|
assert!(
|
||||||
condition.is_none() ||
|
{
|
||||||
(!for_display && condition == Some(ReflowTriggerCondition::PaintPostponed)) ||
|
condition.is_none() ||
|
||||||
self.suppress_reflow.get()
|
(!for_display &&
|
||||||
});
|
condition == Some(ReflowTriggerCondition::PaintPostponed)) ||
|
||||||
|
self.suppress_reflow.get()
|
||||||
|
},
|
||||||
|
"condition was {:?}",
|
||||||
|
condition
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!(
|
||||||
"Document doesn't need reflow - skipping it (reason {:?})",
|
"Document doesn't need reflow - skipping it (reason {:?})",
|
||||||
|
|
|
@ -193,6 +193,8 @@ pub struct ScriptReflow {
|
||||||
pub reflow_info: Reflow,
|
pub reflow_info: Reflow,
|
||||||
/// The document node.
|
/// The document node.
|
||||||
pub document: TrustedNodeAddress,
|
pub document: TrustedNodeAddress,
|
||||||
|
/// The dirty root from which to restyle.
|
||||||
|
pub dirty_root: Option<TrustedNodeAddress>,
|
||||||
/// Whether the document's stylesheets have changed since the last script reflow.
|
/// Whether the document's stylesheets have changed since the last script reflow.
|
||||||
pub stylesheets_changed: bool,
|
pub stylesheets_changed: bool,
|
||||||
/// The current window size.
|
/// The current window size.
|
||||||
|
|
|
@ -63,7 +63,8 @@ pub fn traverse_dom<E, D>(
|
||||||
traversal: &D,
|
traversal: &D,
|
||||||
token: PreTraverseToken<E>,
|
token: PreTraverseToken<E>,
|
||||||
pool: Option<&rayon::ThreadPool>,
|
pool: Option<&rayon::ThreadPool>,
|
||||||
) where
|
) -> E
|
||||||
|
where
|
||||||
E: TElement,
|
E: TElement,
|
||||||
D: DomTraversal<E>,
|
D: DomTraversal<E>,
|
||||||
{
|
{
|
||||||
|
@ -187,4 +188,6 @@ pub fn traverse_dom<E, D>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,10 +30,10 @@ macro_rules! sizeof_checker (
|
||||||
|
|
||||||
// Update the sizes here
|
// Update the sizes here
|
||||||
sizeof_checker!(size_event_target, EventTarget, 56);
|
sizeof_checker!(size_event_target, EventTarget, 56);
|
||||||
sizeof_checker!(size_node, Node, 184);
|
sizeof_checker!(size_node, Node, 192);
|
||||||
sizeof_checker!(size_element, Element, 360);
|
sizeof_checker!(size_element, Element, 368);
|
||||||
sizeof_checker!(size_htmlelement, HTMLElement, 376);
|
sizeof_checker!(size_htmlelement, HTMLElement, 384);
|
||||||
sizeof_checker!(size_div, HTMLDivElement, 376);
|
sizeof_checker!(size_div, HTMLDivElement, 384);
|
||||||
sizeof_checker!(size_span, HTMLSpanElement, 376);
|
sizeof_checker!(size_span, HTMLSpanElement, 384);
|
||||||
sizeof_checker!(size_text, Text, 216);
|
sizeof_checker!(size_text, Text, 224);
|
||||||
sizeof_checker!(size_characterdata, CharacterData, 216);
|
sizeof_checker!(size_characterdata, CharacterData, 224);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue