Auto merge of #13346 - pcwalton:cnn, r=notriddle

layout: Make some major improvements to incremental layout to improve CNN.

CNN is still too slow to be usable, but this is a partial solution.

r? @notriddle (feel free to reassign if you like)

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13346)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-09-26 20:57:59 -05:00 committed by GitHub
commit cbe5458272
7 changed files with 284 additions and 199 deletions

View file

@ -51,6 +51,7 @@ use model::{self, IntrinsicISizes, MarginCollapseInfo};
use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none}; use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none};
use rustc_serialize::{Encodable, Encoder}; use rustc_serialize::{Encodable, Encoder};
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW}; use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
use script_layout_interface::restyle_damage::REPOSITION;
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
@ -1842,6 +1843,8 @@ impl Flow for BlockFlow {
self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size); self.fragment.assign_replaced_block_size_if_necessary(containing_block_block_size);
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) { if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) {
self.base.position.size.block = self.fragment.border_box.size.block; self.base.position.size.block = self.fragment.border_box.size.block;
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
} }
None None
} else if self.is_root() || self.formatting_context_type() != FormattingContextType::None { } else if self.is_root() || self.formatting_context_type() != FormattingContextType::None {
@ -2114,6 +2117,8 @@ impl Flow for BlockFlow {
flow::mut_base(kid).stacking_relative_position_of_display_port = flow::mut_base(kid).stacking_relative_position_of_display_port =
stacking_relative_position_of_display_port_for_children; stacking_relative_position_of_display_port_for_children;
} }
self.base.restyle_damage.remove(REPOSITION)
} }
fn mark_as_root(&mut self) { fn mark_as_root(&mut self) {

View file

@ -42,7 +42,8 @@ use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
use multicol::MulticolFlow; use multicol::MulticolFlow;
use parallel::FlowParallelInfo; use parallel::FlowParallelInfo;
use rustc_serialize::{Encodable, Encoder}; use rustc_serialize::{Encodable, Encoder};
use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, RestyleDamage}; use script_layout_interface::restyle_damage::{RECONSTRUCT_FLOW, REFLOW, REFLOW_OUT_OF_FLOW};
use script_layout_interface::restyle_damage::{REPAINT, REPOSITION, RestyleDamage};
use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode}; use script_layout_interface::wrapper_traits::{PseudoElementType, ThreadSafeLayoutNode};
use std::{fmt, mem, raw}; use std::{fmt, mem, raw};
use std::iter::Zip; use std::iter::Zip;
@ -320,6 +321,7 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
/// Phase 4 of reflow: computes absolute positions. /// Phase 4 of reflow: computes absolute positions.
fn compute_absolute_position(&mut self, _: &SharedLayoutContext) { fn compute_absolute_position(&mut self, _: &SharedLayoutContext) {
// The default implementation is a no-op. // The default implementation is a no-op.
mut_base(self).restyle_damage.remove(REPOSITION)
} }
/// Phase 5 of reflow: builds display lists. /// Phase 5 of reflow: builds display lists.

View file

@ -25,8 +25,8 @@ use gfx_traits::print_tree::PrintTree;
use layout_debug; use layout_debug;
use model::IntrinsicISizesContribution; use model::IntrinsicISizesContribution;
use range::{Range, RangeIndex}; use range::{Range, RangeIndex};
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW}; use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
use script_layout_interface::restyle_damage::{REFLOW_OUT_OF_FLOW, RESOLVE_GENERATED_CONTENT}; use script_layout_interface::restyle_damage::{REPOSITION, RESOLVE_GENERATED_CONTENT};
use script_layout_interface::wrapper_traits::PseudoElementType; use script_layout_interface::wrapper_traits::PseudoElementType;
use std::{fmt, i32, isize, mem}; use std::{fmt, i32, isize, mem};
use std::cmp::max; use std::cmp::max;
@ -1650,6 +1650,8 @@ impl Flow for InlineFlow {
_ => {} _ => {}
} }
} }
self.base.restyle_damage.remove(REPOSITION)
} }
fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {} fn update_late_computed_inline_position_if_necessary(&mut self, _: Au) {}

View file

@ -17,7 +17,7 @@ use generated_content::ResolveGeneratedContent;
use gfx::display_list::{DisplayItem, StackingContext}; use gfx::display_list::{DisplayItem, StackingContext};
use script_layout_interface::restyle_damage::{REFLOW, STORE_OVERFLOW}; use script_layout_interface::restyle_damage::{REFLOW, STORE_OVERFLOW};
use style::context::StyleContext; use style::context::StyleContext;
use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList, ComputeAbsolutePositions}; use traversal::{AssignBSizes, AssignISizes, BubbleISizes, BuildDisplayList};
use util::opts; use util::opts;
pub use style::sequential::traverse_dom; pub use style::sequential::traverse_dom;
@ -78,7 +78,6 @@ pub fn build_display_list_for_subtree(flow_root: &mut Flow,
root_stacking_context: &mut StackingContext, root_stacking_context: &mut StackingContext,
shared_layout_context: &SharedLayoutContext) shared_layout_context: &SharedLayoutContext)
-> Vec<DisplayItem> { -> Vec<DisplayItem> {
flow_root.traverse_preorder(&ComputeAbsolutePositions { layout_context: shared_layout_context });
let mut children = vec![]; let mut children = vec![];
flow_root.collect_stacking_contexts(root_stacking_context.id, flow_root.collect_stacking_contexts(root_stacking_context.id,
&mut children); &mut children);

View file

@ -21,6 +21,7 @@ use gfx_traits::print_tree::PrintTree;
use layout_debug; use layout_debug;
use model::MaybeAuto; use model::MaybeAuto;
use rustc_serialize::{Encodable, Encoder}; use rustc_serialize::{Encodable, Encoder};
use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW};
use std::cmp::max; use std::cmp::max;
use std::fmt; use std::fmt;
use std::iter::{Enumerate, IntoIterator, Peekable}; use std::iter::{Enumerate, IntoIterator, Peekable};
@ -106,73 +107,84 @@ impl TableRowFlow {
/// methods /// methods
#[inline(always)] #[inline(always)]
fn assign_block_size_table_row_base(&mut self, layout_context: &LayoutContext) { fn assign_block_size_table_row_base(&mut self, layout_context: &LayoutContext) {
// Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of all if self.block_flow.base.restyle_damage.contains(REFLOW) {
// cells). // Per CSS 2.1 § 17.5.3, find max_y = max(computed `block-size`, minimum block-size of
let mut max_block_size = Au(0); // all cells).
let thread_id = self.block_flow.base.thread_id; let mut max_block_size = Au(0);
for kid in self.block_flow.base.child_iter_mut() { let thread_id = self.block_flow.base.thread_id;
kid.place_float_if_applicable(); for kid in self.block_flow.base.child_iter_mut() {
if !flow::base(kid).flags.is_float() { kid.place_float_if_applicable();
kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id); if !flow::base(kid).flags.is_float() {
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
thread_id);
}
{
let child_fragment = kid.as_mut_table_cell().fragment();
// TODO: Percentage block-size
let child_specified_block_size =
MaybeAuto::from_style(child_fragment.style().content_block_size(),
Au(0)).specified_or_zero();
max_block_size =
max(max_block_size,
child_specified_block_size +
child_fragment.border_padding.block_start_end());
}
let child_node = flow::mut_base(kid);
child_node.position.start.b = Au(0);
max_block_size = max(max_block_size, child_node.position.size.block);
} }
{ let mut block_size = max_block_size;
let child_fragment = kid.as_mut_table_cell().fragment(); // TODO: Percentage block-size
// TODO: Percentage block-size block_size = match MaybeAuto::from_style(self.block_flow
let child_specified_block_size = .fragment
MaybeAuto::from_style(child_fragment.style().content_block_size(), .style()
Au(0)).specified_or_zero(); .content_block_size(),
max_block_size = Au(0)) {
max(max_block_size, MaybeAuto::Auto => block_size,
child_specified_block_size + MaybeAuto::Specified(value) => max(value, block_size),
child_fragment.border_padding.block_start_end()); };
// Assign the block-size of own fragment
let mut position = self.block_flow.fragment.border_box;
position.size.block = block_size;
self.block_flow.fragment.border_box = position;
self.block_flow.base.position.size.block = block_size;
// Assign the block-size of kid fragments, which is the same value as own block-size.
for kid in self.block_flow.base.child_iter_mut() {
let child_table_cell = kid.as_mut_table_cell();
{
let kid_fragment = child_table_cell.mut_fragment();
let mut position = kid_fragment.border_box;
position.size.block = block_size;
kid_fragment.border_box = position;
}
// Assign the child's block size.
child_table_cell.block_flow.base.position.size.block = block_size;
// Now we know the cell height, vertical align the cell's children.
child_table_cell.valign_children();
// Write in the size of the relative containing block for children. (This
// information is also needed to handle RTL.)
child_table_cell.block_flow.base.early_absolute_position_info =
EarlyAbsolutePositionInfo {
relative_containing_block_size: self.block_flow
.fragment
.content_box()
.size,
relative_containing_block_mode: self.block_flow
.fragment
.style()
.writing_mode,
};
} }
let child_node = flow::mut_base(kid);
child_node.position.start.b = Au(0);
max_block_size = max(max_block_size, child_node.position.size.block);
} }
let mut block_size = max_block_size; self.block_flow.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
// TODO: Percentage block-size
block_size = match MaybeAuto::from_style(self.block_flow
.fragment
.style()
.content_block_size(),
Au(0)) {
MaybeAuto::Auto => block_size,
MaybeAuto::Specified(value) => max(value, block_size),
};
// Assign the block-size of own fragment
let mut position = self.block_flow.fragment.border_box;
position.size.block = block_size;
self.block_flow.fragment.border_box = position;
self.block_flow.base.position.size.block = block_size;
// Assign the block-size of kid fragments, which is the same value as own block-size.
for kid in self.block_flow.base.child_iter_mut() {
let child_table_cell = kid.as_mut_table_cell();
{
let kid_fragment = child_table_cell.mut_fragment();
let mut position = kid_fragment.border_box;
position.size.block = block_size;
kid_fragment.border_box = position;
}
// Assign the child's block size.
child_table_cell.block_flow.base.position.size.block = block_size;
// Now we know the cell height, vertical align the cell's children.
child_table_cell.valign_children();
// Write in the size of the relative containing block for children. (This information
// is also needed to handle RTL.)
child_table_cell.block_flow.base.early_absolute_position_info =
EarlyAbsolutePositionInfo {
relative_containing_block_size: self.block_flow.fragment.content_box().size,
relative_containing_block_mode: self.block_flow.fragment.style().writing_mode,
};
}
} }
pub fn populate_collapsed_border_spacing<'a, I>( pub fn populate_collapsed_border_spacing<'a, I>(

View file

@ -65,7 +65,7 @@ use layout::animation;
use layout::construct::ConstructionResult; use layout::construct::ConstructionResult;
use layout::context::{LayoutContext, SharedLayoutContext, heap_size_of_local_context}; use layout::context::{LayoutContext, SharedLayoutContext, heap_size_of_local_context};
use layout::display_list_builder::ToGfxColor; use layout::display_list_builder::ToGfxColor;
use layout::flow::{self, Flow, ImmutableFlowUtils, MutableOwnedFlowUtils}; use layout::flow::{self, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
use layout::flow_ref::{self, FlowRef}; use layout::flow_ref::{self, FlowRef};
use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT}; use layout::incremental::{LayoutDamageComputation, REFLOW_ENTIRE_DOCUMENT};
use layout::layout_debug; use layout::layout_debug;
@ -75,7 +75,7 @@ use layout::query::{process_margin_style_query, process_node_overflow_request, p
use layout::query::{process_node_geometry_request, process_node_layer_id_request, process_node_scroll_area_request}; use layout::query::{process_node_geometry_request, process_node_layer_id_request, process_node_scroll_area_request};
use layout::query::process_offset_parent_query; use layout::query::process_offset_parent_query;
use layout::sequential; use layout::sequential;
use layout::traversal::RecalcStyleAndConstructFlows; use layout::traversal::{ComputeAbsolutePositions, RecalcStyleAndConstructFlows};
use layout::webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBuilder}; use layout::webrender_helpers::{WebRenderDisplayListConverter, WebRenderFrameBuilder};
use layout::wrapper::{LayoutNodeLayoutData, NonOpaqueStyleAndLayoutData}; use layout::wrapper::{LayoutNodeLayoutData, NonOpaqueStyleAndLayoutData};
use layout_traits::LayoutThreadFactory; use layout_traits::LayoutThreadFactory;
@ -89,7 +89,8 @@ use script::layout_wrapper::{ServoLayoutDocument, ServoLayoutNode};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData}; use script_layout_interface::{OpaqueStyleAndLayoutData, PartialStyleAndLayoutData};
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow}; use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
use script_layout_interface::reporter::CSSErrorReporter; use script_layout_interface::reporter::CSSErrorReporter;
use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, STORE_OVERFLOW}; use script_layout_interface::restyle_damage::{REFLOW, REFLOW_OUT_OF_FLOW, REPAINT, REPOSITION};
use script_layout_interface::restyle_damage::STORE_OVERFLOW;
use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse}; use script_layout_interface::rpc::{LayoutRPC, MarginStyleResponse, NodeOverflowResponse, OffsetParentResponse};
use script_layout_interface::wrapper_traits::LayoutNode; use script_layout_interface::wrapper_traits::LayoutNode;
use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg}; use script_traits::{ConstellationControlMsg, LayoutControlMsg, LayoutMsg as ConstellationMsg};
@ -629,6 +630,7 @@ impl LayoutThread {
reflow_info.goal); reflow_info.goal);
self.perform_post_style_recalc_layout_passes(&reflow_info, self.perform_post_style_recalc_layout_passes(&reflow_info,
None,
None, None,
&mut *rw_data, &mut *rw_data,
&mut layout_context); &mut layout_context);
@ -900,8 +902,11 @@ impl LayoutThread {
traversal); traversal);
} }
/// Computes the stacking-relative positions of all flows and, if the painting is dirty and the
/// reflow goal and query type need it, builds the display list.
fn compute_abs_pos_and_build_display_list(&mut self, fn compute_abs_pos_and_build_display_list(&mut self,
data: &Reflow, data: &Reflow,
query_type: Option<&ReflowQueryType>,
document: Option<&ServoLayoutDocument>, document: Option<&ServoLayoutDocument>,
layout_root: &mut Flow, layout_root: &mut Flow,
shared_layout_context: &mut SharedLayoutContext, shared_layout_context: &mut SharedLayoutContext,
@ -919,49 +924,64 @@ impl LayoutThread {
flow::mut_base(layout_root).clip = flow::mut_base(layout_root).clip =
ClippingRegion::from_rect(&data.page_clip_rect); ClippingRegion::from_rect(&data.page_clip_rect);
if flow::base(layout_root).restyle_damage.contains(REPOSITION) {
layout_root.traverse_preorder(&ComputeAbsolutePositions {
layout_context: shared_layout_context
});
}
if flow::base(layout_root).restyle_damage.contains(REPAINT) || if flow::base(layout_root).restyle_damage.contains(REPAINT) ||
rw_data.display_list.is_none() { rw_data.display_list.is_none() {
let mut root_stacking_context = StackingContext::new(StackingContextId::new(0), let display_list_needed = query_type.map(reflow_query_type_needs_display_list)
StackingContextType::Real, .unwrap_or(false);
&Rect::zero(), match (data.goal, display_list_needed) {
&Rect::zero(), (ReflowGoal::ForDisplay, _) | (ReflowGoal::ForScriptQuery, true) => {
0, let mut root_stacking_context =
filter::T::new(Vec::new()), StackingContext::new(StackingContextId::new(0),
mix_blend_mode::T::normal, StackingContextType::Real,
Matrix4D::identity(), &Rect::zero(),
Matrix4D::identity(), &Rect::zero(),
true, 0,
false, filter::T::new(Vec::new()),
None); mix_blend_mode::T::normal,
Matrix4D::identity(),
Matrix4D::identity(),
true,
false,
None);
let display_list_entries = let display_list_entries =
sequential::build_display_list_for_subtree(layout_root, sequential::build_display_list_for_subtree(layout_root,
&mut root_stacking_context, &mut root_stacking_context,
shared_layout_context); shared_layout_context);
debug!("Done building display list."); debug!("Done building display list.");
let root_background_color = get_root_flow_background_color(layout_root); let root_background_color = get_root_flow_background_color(layout_root);
let root_size = { let root_size = {
let root_flow = flow::base(layout_root); let root_flow = flow::base(layout_root);
if rw_data.stylist.viewport_constraints().is_some() { if rw_data.stylist.viewport_constraints().is_some() {
root_flow.position.size.to_physical(root_flow.writing_mode) root_flow.position.size.to_physical(root_flow.writing_mode)
} else { } else {
root_flow.overflow.scroll.size root_flow.overflow.scroll.size
}
};
let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size);
root_stacking_context.bounds = origin;
root_stacking_context.overflow = origin;
root_stacking_context.layer_info =
Some(LayerInfo::new(layout_root.layer_id(),
ScrollPolicy::Scrollable,
None,
root_background_color));
rw_data.display_list =
Some(Arc::new(DisplayList::new(root_stacking_context,
display_list_entries)))
} }
}; (ReflowGoal::ForScriptQuery, false) => {}
}
let origin = Rect::new(Point2D::new(Au(0), Au(0)), root_size);
root_stacking_context.bounds = origin;
root_stacking_context.overflow = origin;
root_stacking_context.layer_info =
Some(LayerInfo::new(layout_root.layer_id(),
ScrollPolicy::Scrollable,
None,
root_background_color));
rw_data.display_list =
Some(Arc::new(DisplayList::new(root_stacking_context, display_list_entries)))
} }
if data.goal != ReflowGoal::ForDisplay { if data.goal != ReflowGoal::ForDisplay {
@ -1038,6 +1058,12 @@ impl LayoutThread {
let document = unsafe { ServoLayoutNode::new(&data.document) }; let document = unsafe { ServoLayoutNode::new(&data.document) };
let document = document.as_document().unwrap(); let document = document.as_document().unwrap();
// FIXME(pcwalton): Combine `ReflowGoal` and `ReflowQueryType`. Then remove this assert.
debug_assert!((data.reflow_info.goal == ReflowGoal::ForDisplay &&
data.query_type == ReflowQueryType::NoQuery) ||
(data.reflow_info.goal == ReflowGoal::ForScriptQuery &&
data.query_type != ReflowQueryType::NoQuery));
debug!("layout: received layout request for: {}", self.url); debug!("layout: received layout request for: {}", self.url);
let mut rw_data = possibly_locked_rw_data.lock(); let mut rw_data = possibly_locked_rw_data.lock();
@ -1217,74 +1243,84 @@ impl LayoutThread {
// Perform post-style recalculation layout passes. // Perform post-style recalculation layout passes.
self.perform_post_style_recalc_layout_passes(&data.reflow_info, self.perform_post_style_recalc_layout_passes(&data.reflow_info,
Some(&data.query_type),
Some(&document), Some(&document),
&mut rw_data, &mut rw_data,
&mut shared_layout_context); &mut shared_layout_context);
if let Some(mut root_flow) = self.root_flow.clone() { self.respond_to_query_if_necessary(&data.query_type,
let root_flow = flow_ref::deref_mut(&mut root_flow); &mut *rw_data,
match data.query_type { &mut shared_layout_context);
ReflowQueryType::ContentBoxQuery(node) => { }
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.content_box_response = process_content_box_request(node, root_flow);
},
ReflowQueryType::ContentBoxesQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
},
ReflowQueryType::HitTestQuery(translated_point, client_point, update_cursor) => {
let translated_point =
Point2D::new(Au::from_f32_px(translated_point.x),
Au::from_f32_px(translated_point.y));
let client_point = fn respond_to_query_if_necessary(&mut self,
Point2D::new(Au::from_f32_px(client_point.x), query_type: &ReflowQueryType,
Au::from_f32_px(client_point.y)); rw_data: &mut LayoutThreadData,
shared_layout_context: &mut SharedLayoutContext) {
let mut root_flow = match self.root_flow.clone() {
Some(root_flow) => root_flow,
None => return,
};
let root_flow = flow_ref::deref_mut(&mut root_flow);
match *query_type {
ReflowQueryType::ContentBoxQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.content_box_response = process_content_box_request(node, root_flow);
},
ReflowQueryType::ContentBoxesQuery(node) => {
let node = unsafe { ServoLayoutNode::new(&node) };
rw_data.content_boxes_response = process_content_boxes_request(node, root_flow);
},
ReflowQueryType::HitTestQuery(translated_point, client_point, update_cursor) => {
let translated_point = Point2D::new(Au::from_f32_px(translated_point.x),
Au::from_f32_px(translated_point.y));
let result = rw_data.display_list let client_point = Point2D::new(Au::from_f32_px(client_point.x),
.as_ref() Au::from_f32_px(client_point.y));
.expect("Tried to hit test with no display list")
.hit_test(&translated_point, let result = rw_data.display_list
&client_point, .as_ref()
&rw_data.stacking_context_scroll_offsets); .expect("Tried to hit test with no display list")
rw_data.hit_test_response = (result.last().cloned(), update_cursor); .hit_test(&translated_point,
}, &client_point,
ReflowQueryType::NodeGeometryQuery(node) => { &rw_data.stacking_context_scroll_offsets);
let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.hit_test_response = (result.last().cloned(), update_cursor);
rw_data.client_rect_response = process_node_geometry_request(node, root_flow); },
}, ReflowQueryType::NodeGeometryQuery(node) => {
ReflowQueryType::NodeScrollGeometryQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) };
let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.client_rect_response = process_node_geometry_request(node, root_flow);
rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow); },
}, ReflowQueryType::NodeScrollGeometryQuery(node) => {
ReflowQueryType::NodeOverflowQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) };
let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.scroll_area_response = process_node_scroll_area_request(node, root_flow);
rw_data.overflow_response = process_node_overflow_request(node); },
}, ReflowQueryType::NodeOverflowQuery(node) => {
ReflowQueryType::NodeLayerIdQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) };
let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.overflow_response = process_node_overflow_request(node);
rw_data.layer_id_response = Some(process_node_layer_id_request(node)); },
}, ReflowQueryType::NodeLayerIdQuery(node) => {
ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => { let node = unsafe { ServoLayoutNode::new(&node) };
let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.layer_id_response = Some(process_node_layer_id_request(node));
let layout_context = LayoutContext::new(&shared_layout_context); },
rw_data.resolved_style_response = ReflowQueryType::ResolvedStyleQuery(node, ref pseudo, ref property) => {
process_resolved_style_request(node, let node = unsafe { ServoLayoutNode::new(&node) };
&layout_context, let layout_context = LayoutContext::new(&shared_layout_context);
pseudo, rw_data.resolved_style_response =
property, process_resolved_style_request(node,
root_flow); &layout_context,
}, pseudo,
ReflowQueryType::OffsetParentQuery(node) => { property,
let node = unsafe { ServoLayoutNode::new(&node) }; root_flow);
rw_data.offset_parent_response = process_offset_parent_query(node, root_flow); },
}, ReflowQueryType::OffsetParentQuery(node) => {
ReflowQueryType::MarginStyleQuery(node) => { let node = unsafe { ServoLayoutNode::new(&node) };
let node = unsafe { ServoLayoutNode::new(&node) }; rw_data.offset_parent_response = process_offset_parent_query(node, root_flow);
rw_data.margin_style_response = process_margin_style_query(node); },
}, ReflowQueryType::MarginStyleQuery(node) => {
ReflowQueryType::NoQuery => {} let node = unsafe { ServoLayoutNode::new(&node) };
} rw_data.margin_style_response = process_margin_style_query(node);
},
ReflowQueryType::NoQuery => {}
} }
} }
@ -1340,7 +1376,11 @@ impl LayoutThread {
false, false,
reflow_info.goal); reflow_info.goal);
self.perform_post_main_layout_passes(&reflow_info, None, &mut *rw_data, &mut layout_context); self.perform_post_main_layout_passes(&reflow_info,
None,
None,
&mut *rw_data,
&mut layout_context);
true true
} }
@ -1398,6 +1438,7 @@ impl LayoutThread {
} }
self.perform_post_style_recalc_layout_passes(&reflow_info, self.perform_post_style_recalc_layout_passes(&reflow_info,
None,
None, None,
&mut *rw_data, &mut *rw_data,
&mut layout_context); &mut layout_context);
@ -1421,6 +1462,7 @@ impl LayoutThread {
return return
} }
self.perform_post_style_recalc_layout_passes(&reflow_info, self.perform_post_style_recalc_layout_passes(&reflow_info,
None,
None, None,
&mut *rw_data, &mut *rw_data,
&mut layout_context); &mut layout_context);
@ -1428,6 +1470,7 @@ impl LayoutThread {
fn perform_post_style_recalc_layout_passes(&mut self, fn perform_post_style_recalc_layout_passes(&mut self,
data: &Reflow, data: &Reflow,
query_type: Option<&ReflowQueryType>,
document: Option<&ServoLayoutDocument>, document: Option<&ServoLayoutDocument>,
rw_data: &mut LayoutThreadData, rw_data: &mut LayoutThreadData,
layout_context: &mut SharedLayoutContext) { layout_context: &mut SharedLayoutContext) {
@ -1503,18 +1546,24 @@ impl LayoutThread {
flow_ref::deref_mut(&mut root_flow) as &mut Flow); flow_ref::deref_mut(&mut root_flow) as &mut Flow);
}); });
self.perform_post_main_layout_passes(data, document, rw_data, layout_context); self.perform_post_main_layout_passes(data,
query_type,
document,
rw_data,
layout_context);
} }
} }
fn perform_post_main_layout_passes(&mut self, fn perform_post_main_layout_passes(&mut self,
data: &Reflow, data: &Reflow,
query_type: Option<&ReflowQueryType>,
document: Option<&ServoLayoutDocument>, document: Option<&ServoLayoutDocument>,
rw_data: &mut LayoutThreadData, rw_data: &mut LayoutThreadData,
layout_context: &mut SharedLayoutContext) { layout_context: &mut SharedLayoutContext) {
// Build the display list if necessary, and send it to the painter. // Build the display list if necessary, and send it to the painter.
if let Some(mut root_flow) = self.root_flow.clone() { if let Some(mut root_flow) = self.root_flow.clone() {
self.compute_abs_pos_and_build_display_list(data, self.compute_abs_pos_and_build_display_list(data,
query_type,
document, document,
flow_ref::deref_mut(&mut root_flow), flow_ref::deref_mut(&mut root_flow),
&mut *layout_context, &mut *layout_context,
@ -1632,6 +1681,18 @@ fn get_ua_stylesheets() -> Result<UserAgentStylesheets, &'static str> {
}) })
} }
/// Returns true if the given reflow query type needs a full, up-to-date display list to be present
/// or false if it only needs stacking-relative positions.
fn reflow_query_type_needs_display_list(query_type: &ReflowQueryType) -> bool {
match *query_type {
ReflowQueryType::HitTestQuery(..) => true,
ReflowQueryType::ContentBoxQuery(_) | ReflowQueryType::ContentBoxesQuery(_) |
ReflowQueryType::NodeGeometryQuery(_) | ReflowQueryType::NodeScrollGeometryQuery(_) |
ReflowQueryType::NodeOverflowQuery(_) | ReflowQueryType::NodeLayerIdQuery(_) |
ReflowQueryType::ResolvedStyleQuery(..) | ReflowQueryType::OffsetParentQuery(_) |
ReflowQueryType::MarginStyleQuery(_) | ReflowQueryType::NoQuery => false,
}
}
lazy_static! { lazy_static! {
static ref UA_STYLESHEETS: UserAgentStylesheets = { static ref UA_STYLESHEETS: UserAgentStylesheets = {

View file

@ -15,31 +15,36 @@ bitflags! {
#[doc = "Currently unused; need to decide how this propagates."] #[doc = "Currently unused; need to decide how this propagates."]
const REPAINT = 0x01, const REPAINT = 0x01,
#[doc = "The stacking-context-relative position of this node or its descendants has \
changed."]
#[doc = "Propagates both up and down the flow tree."]
const REPOSITION = 0x02,
#[doc = "Recompute the overflow regions (bounding box of object and all descendants)."] #[doc = "Recompute the overflow regions (bounding box of object and all descendants)."]
#[doc = "Propagates down the flow tree because the computation is bottom-up."] #[doc = "Propagates down the flow tree because the computation is bottom-up."]
const STORE_OVERFLOW = 0x02, const STORE_OVERFLOW = 0x04,
#[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."] #[doc = "Recompute intrinsic inline_sizes (minimum and preferred)."]
#[doc = "Propagates down the flow tree because the computation is"] #[doc = "Propagates down the flow tree because the computation is"]
#[doc = "bottom-up."] #[doc = "bottom-up."]
const BUBBLE_ISIZES = 0x04, const BUBBLE_ISIZES = 0x08,
#[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \ #[doc = "Recompute actual inline-sizes and block-sizes, only taking out-of-flow children \
into account. \ into account. \
Propagates up the flow tree because the computation is top-down."] Propagates up the flow tree because the computation is top-down."]
const REFLOW_OUT_OF_FLOW = 0x08, const REFLOW_OUT_OF_FLOW = 0x10,
#[doc = "Recompute actual inline_sizes and block_sizes."] #[doc = "Recompute actual inline_sizes and block_sizes."]
#[doc = "Propagates up the flow tree because the computation is"] #[doc = "Propagates up the flow tree because the computation is"]
#[doc = "top-down."] #[doc = "top-down."]
const REFLOW = 0x10, const REFLOW = 0x20,
#[doc = "Re-resolve generated content. \ #[doc = "Re-resolve generated content. \
Propagates up the flow tree because the computation is inorder."] Propagates up the flow tree because the computation is inorder."]
const RESOLVE_GENERATED_CONTENT = 0x20, const RESOLVE_GENERATED_CONTENT = 0x40,
#[doc = "The entire flow needs to be reconstructed."] #[doc = "The entire flow needs to be reconstructed."]
const RECONSTRUCT_FLOW = 0x40 const RECONSTRUCT_FLOW = 0x80
} }
} }
@ -63,7 +68,8 @@ impl TRestyleDamage for RestyleDamage {
/// `RestyleDamage::all()` will result in unnecessary sequential resolution /// `RestyleDamage::all()` will result in unnecessary sequential resolution
/// of generated content. /// of generated content.
fn rebuild_and_reflow() -> RestyleDamage { fn rebuild_and_reflow() -> RestyleDamage {
REPAINT | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW | RECONSTRUCT_FLOW REPAINT | REPOSITION | STORE_OVERFLOW | BUBBLE_ISIZES | REFLOW_OUT_OF_FLOW | REFLOW |
RECONSTRUCT_FLOW
} }
} }
@ -72,9 +78,10 @@ impl RestyleDamage {
/// returns the damage that we should add to the *parent* of this flow. /// returns the damage that we should add to the *parent* of this flow.
pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage { pub fn damage_for_parent(self, child_is_absolutely_positioned: bool) -> RestyleDamage {
if child_is_absolutely_positioned { if child_is_absolutely_positioned {
self & (REPAINT | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW | RESOLVE_GENERATED_CONTENT) self & (REPAINT | REPOSITION | STORE_OVERFLOW | REFLOW_OUT_OF_FLOW |
RESOLVE_GENERATED_CONTENT)
} else { } else {
self & (REPAINT | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW | self & (REPAINT | REPOSITION | STORE_OVERFLOW | REFLOW | REFLOW_OUT_OF_FLOW |
RESOLVE_GENERATED_CONTENT) RESOLVE_GENERATED_CONTENT)
} }
} }
@ -90,7 +97,7 @@ impl RestyleDamage {
// Absolute children are out-of-flow and therefore insulated from changes. // Absolute children are out-of-flow and therefore insulated from changes.
// //
// FIXME(pcwalton): Au contraire, if the containing block dimensions change! // FIXME(pcwalton): Au contraire, if the containing block dimensions change!
self & REPAINT self & (REPAINT | REPOSITION)
} }
(true, false) => { (true, false) => {
// Changing the position of an absolutely-positioned block requires us to reflow // Changing the position of an absolutely-positioned block requires us to reflow
@ -103,7 +110,7 @@ impl RestyleDamage {
} }
_ => { _ => {
// TODO(pcwalton): Take floatedness into account. // TODO(pcwalton): Take floatedness into account.
self & (REPAINT | REFLOW) self & (REPAINT | REPOSITION | REFLOW)
} }
} }
} }
@ -115,6 +122,7 @@ impl fmt::Display for RestyleDamage {
let to_iter = let to_iter =
[ (REPAINT, "Repaint") [ (REPAINT, "Repaint")
, (REPOSITION, "Reposition")
, (STORE_OVERFLOW, "StoreOverflow") , (STORE_OVERFLOW, "StoreOverflow")
, (BUBBLE_ISIZES, "BubbleISizes") , (BUBBLE_ISIZES, "BubbleISizes")
, (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow") , (REFLOW_OUT_OF_FLOW, "ReflowOutOfFlow")
@ -163,14 +171,8 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Resty
// FIXME: Test somehow that every property is included. // FIXME: Test somehow that every property is included.
add_if_not_equal!(old, new, damage, add_if_not_equal!(old, new, damage,
[ [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW,
REPAINT, REFLOW, RECONSTRUCT_FLOW], [
STORE_OVERFLOW,
BUBBLE_ISIZES,
REFLOW_OUT_OF_FLOW,
REFLOW,
RECONSTRUCT_FLOW
], [
get_box.float, get_box.display, get_box.position, get_counters.content, get_box.float, get_box.display, get_box.position, get_counters.content,
get_counters.counter_reset, get_counters.counter_increment, get_counters.counter_reset, get_counters.counter_increment,
get_inheritedbox._servo_under_display_none, get_inheritedbox._servo_under_display_none,
@ -191,8 +193,8 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Resty
get_column.column_width, get_column.column_count get_column.column_width, get_column.column_count
]) || (new.get_box().display == display::T::inline && ]) || (new.get_box().display == display::T::inline &&
add_if_not_equal!(old, new, damage, add_if_not_equal!(old, new, damage,
[REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW, [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES,
RECONSTRUCT_FLOW], [ REFLOW_OUT_OF_FLOW, REFLOW, RECONSTRUCT_FLOW], [
// For inline boxes only, border/padding styles are used in flow construction (to decide // For inline boxes only, border/padding styles are used in flow construction (to decide
// whether to create fragments for empty flows). // whether to create fragments for empty flows).
get_border.border_top_width, get_border.border_right_width, get_border.border_top_width, get_border.border_right_width,
@ -200,7 +202,8 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Resty
get_padding.padding_top, get_padding.padding_right, get_padding.padding_top, get_padding.padding_right,
get_padding.padding_bottom, get_padding.padding_left get_padding.padding_bottom, get_padding.padding_left
])) || add_if_not_equal!(old, new, damage, ])) || add_if_not_equal!(old, new, damage,
[ REPAINT, STORE_OVERFLOW, BUBBLE_ISIZES, REFLOW_OUT_OF_FLOW, REFLOW ], [REPAINT, REPOSITION, STORE_OVERFLOW, BUBBLE_ISIZES,
REFLOW_OUT_OF_FLOW, REFLOW],
[get_border.border_top_width, get_border.border_right_width, [get_border.border_top_width, get_border.border_right_width,
get_border.border_bottom_width, get_border.border_left_width, get_border.border_bottom_width, get_border.border_left_width,
get_margin.margin_top, get_margin.margin_right, get_margin.margin_top, get_margin.margin_right,
@ -225,11 +228,14 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Resty
get_position.flex_shrink, get_position.flex_shrink,
get_position.align_self get_position.align_self
]) || add_if_not_equal!(old, new, damage, ]) || add_if_not_equal!(old, new, damage,
[ REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW ], [ [REPAINT, REPOSITION, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW], [
get_position.top, get_position.left, get_position.top, get_position.left,
get_position.right, get_position.bottom get_position.right, get_position.bottom,
get_effects.opacity,
get_effects.transform, get_effects.transform_style, get_effects.transform_origin,
get_effects.perspective, get_effects.perspective_origin
]) || add_if_not_equal!(old, new, damage, ]) || add_if_not_equal!(old, new, damage,
[ REPAINT ], [ [REPAINT], [
get_color.color, get_background.background_color, get_color.color, get_background.background_color,
get_background.background_image, get_background.background_position, get_background.background_image, get_background.background_position,
get_background.background_repeat, get_background.background_attachment, get_background.background_repeat, get_background.background_attachment,
@ -245,9 +251,7 @@ fn compute_damage(old: &ServoComputedValues, new: &ServoComputedValues) -> Resty
get_inheritedtext._servo_text_decorations_in_effect, get_inheritedtext._servo_text_decorations_in_effect,
get_pointing.cursor, get_pointing.pointer_events, get_pointing.cursor, get_pointing.pointer_events,
get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter, get_effects.box_shadow, get_effects.clip, get_inheritedtext.text_shadow, get_effects.filter,
get_effects.transform, get_effects.backface_visibility, get_effects.transform_style, get_effects.mix_blend_mode, get_inheritedbox.image_rendering,
get_effects.transform_origin, get_effects.perspective, get_effects.perspective_origin,
get_effects.mix_blend_mode, get_effects.opacity, get_inheritedbox.image_rendering,
// Note: May require REFLOW et al. if `visibility: collapse` is implemented. // Note: May require REFLOW et al. if `visibility: collapse` is implemented.
get_inheritedbox.visibility get_inheritedbox.visibility