mirror of
https://github.com/servo/servo.git
synced 2025-07-24 15:50:21 +01:00
Have ContentBox(es)Queries consult the flow tree
Instead of looking at the display tree, have ContentBox(es)Query consult the flow tree. This allow optimizing away parts of the display tree later. To do this we need to be more careful about how we send reflow requests, only querying the flow tree when possible. Fixes #3790.
This commit is contained in:
parent
1a3ff8739c
commit
2d72f00ccf
19 changed files with 374 additions and 213 deletions
|
@ -35,7 +35,8 @@ use floats::{ClearBoth, ClearLeft, ClearRight, FloatKind, FloatLeft, Floats, Pla
|
||||||
use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
|
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::{Fragment, ImageFragment, InlineBlockFragment, ScannedTextFragment};
|
use fragment::{Fragment, ImageFragment, InlineBlockFragment, FragmentBoundsIterator};
|
||||||
|
use fragment::ScannedTextFragment;
|
||||||
use incremental::{Reflow, ReflowOutOfFlow};
|
use incremental::{Reflow, ReflowOutOfFlow};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough};
|
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse, MarginsCollapseThrough};
|
||||||
|
@ -1816,6 +1817,14 @@ impl Flow for BlockFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.fragment.repair_style(new_style)
|
self.fragment.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
if iterator.should_process(&self.fragment) {
|
||||||
|
let fragment_origin = self.base.child_fragment_absolute_position(&self.fragment);
|
||||||
|
iterator.process(&self.fragment,
|
||||||
|
self.fragment.abs_bounds_from_origin(&fragment_origin));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for BlockFlow {
|
impl fmt::Show for BlockFlow {
|
||||||
|
|
|
@ -322,7 +322,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
Rect(physical_rect.origin + flow_origin, physical_rect.size)
|
Rect(physical_rect.origin + flow_origin, physical_rect.size)
|
||||||
};
|
};
|
||||||
// Fragment position wrt to the owning flow.
|
// Fragment position wrt to the owning flow.
|
||||||
let absolute_fragment_bounds = rect_to_absolute(self.style.writing_mode, self.border_box);
|
let absolute_fragment_bounds = self.abs_bounds_from_origin(&flow_origin);
|
||||||
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
|
debug!("Fragment::build_display_list at rel={}, abs={}: {}",
|
||||||
self.border_box,
|
self.border_box,
|
||||||
absolute_fragment_bounds,
|
absolute_fragment_bounds,
|
||||||
|
@ -615,18 +615,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
fn build_display_list_for_block(&mut self,
|
fn build_display_list_for_block(&mut self,
|
||||||
layout_context: &LayoutContext,
|
layout_context: &LayoutContext,
|
||||||
background_border_level: BackgroundAndBorderLevel) {
|
background_border_level: BackgroundAndBorderLevel) {
|
||||||
let relative_offset =
|
|
||||||
self.fragment.relative_position(&self.base
|
|
||||||
.absolute_position_info
|
|
||||||
.relative_containing_block_size);
|
|
||||||
|
|
||||||
// Add the box that starts the block context.
|
// Add the box that starts the block context.
|
||||||
self.base.display_list = DisplayList::new();
|
let absolute_fragment_origin = self.base.child_fragment_absolute_position(&self.fragment);
|
||||||
let absolute_position =
|
|
||||||
self.base.abs_position.add_size(&relative_offset.to_physical(self.base.writing_mode));
|
|
||||||
self.fragment.build_display_list(&mut self.base.display_list,
|
self.fragment.build_display_list(&mut self.base.display_list,
|
||||||
layout_context,
|
layout_context,
|
||||||
absolute_position,
|
absolute_fragment_origin,
|
||||||
background_border_level,
|
background_border_level,
|
||||||
&self.base.clip_rect);
|
&self.base.clip_rect);
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,7 @@ use context::LayoutContext;
|
||||||
use floats::Floats;
|
use floats::Floats;
|
||||||
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
|
use flow_list::{FlowList, FlowListIterator, MutFlowListIterator};
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
use fragment::{Fragment, TableRowFragment, TableCellFragment};
|
use fragment::{Fragment, FragmentBoundsIterator, TableRowFragment, TableCellFragment};
|
||||||
use incremental::{ReconstructFlow, Reflow, ReflowOutOfFlow, RestyleDamage};
|
use incremental::{ReconstructFlow, Reflow, ReflowOutOfFlow, RestyleDamage};
|
||||||
use inline::InlineFlow;
|
use inline::InlineFlow;
|
||||||
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
use model::{CollapsibleMargins, IntrinsicISizes, MarginCollapseInfo};
|
||||||
|
@ -214,6 +214,9 @@ pub trait Flow: fmt::Show + ToString + Sync {
|
||||||
/// Phase 5 of reflow: builds display lists.
|
/// Phase 5 of reflow: builds display lists.
|
||||||
fn build_display_list(&mut self, layout_context: &LayoutContext);
|
fn build_display_list(&mut self, layout_context: &LayoutContext);
|
||||||
|
|
||||||
|
/// Perform an iteration of fragment bounds on this flow.
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator);
|
||||||
|
|
||||||
fn compute_collapsible_block_start_margin(&mut self,
|
fn compute_collapsible_block_start_margin(&mut self,
|
||||||
_layout_context: &mut LayoutContext,
|
_layout_context: &mut LayoutContext,
|
||||||
_margin_collapse_info: &mut MarginCollapseInfo) {
|
_margin_collapse_info: &mut MarginCollapseInfo) {
|
||||||
|
@ -943,6 +946,14 @@ impl BaseFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn child_fragment_absolute_position(&self, fragment: &Fragment) -> Point2D<Au> {
|
||||||
|
let relative_offset =
|
||||||
|
fragment.relative_position(&self
|
||||||
|
.absolute_position_info
|
||||||
|
.relative_containing_block_size);
|
||||||
|
self.abs_position.add_size(&relative_offset.to_physical(self.writing_mode))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
|
impl<'a> ImmutableFlowUtils for &'a Flow + 'a {
|
||||||
|
|
|
@ -22,7 +22,7 @@ use text;
|
||||||
use util::OpaqueNodeMethods;
|
use util::OpaqueNodeMethods;
|
||||||
use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
|
use wrapper::{TLayoutNode, ThreadSafeLayoutNode};
|
||||||
|
|
||||||
use geom::Size2D;
|
use geom::{Point2D, Rect, Size2D};
|
||||||
use gfx::display_list::OpaqueNode;
|
use gfx::display_list::OpaqueNode;
|
||||||
use gfx::text::glyph::CharIndex;
|
use gfx::text::glyph::CharIndex;
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
|
@ -1482,6 +1482,13 @@ impl Fragment {
|
||||||
pub fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
pub fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.style = (*new_style).clone()
|
self.style = (*new_style).clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn abs_bounds_from_origin(&self, fragment_origin: &Point2D<Au>) -> Rect<Au> {
|
||||||
|
// FIXME(#2795): Get the real container size
|
||||||
|
let container_size = Size2D::zero();
|
||||||
|
self.border_box.to_physical(self.style.writing_mode, container_size)
|
||||||
|
.translate(fragment_origin)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for Fragment {
|
impl fmt::Show for Fragment {
|
||||||
|
@ -1502,3 +1509,13 @@ bitflags! {
|
||||||
static IntrinsicInlineSizeIncludesSpecified = 0x08,
|
static IntrinsicInlineSizeIncludesSpecified = 0x08,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A top-down fragment bounds iteration handler.
|
||||||
|
pub trait FragmentBoundsIterator {
|
||||||
|
/// The operation to perform.
|
||||||
|
fn process(&mut self, fragment: &Fragment, bounds: Rect<Au>);
|
||||||
|
|
||||||
|
/// Returns true if this fragment must be processed in-order. If this returns false,
|
||||||
|
/// we skip the operation for this fragment, but continue processing siblings.
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool;
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,8 @@ use floats::{FloatLeft, Floats, PlacementInfo};
|
||||||
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
|
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass, MutableFlowUtils};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
use fragment::{Fragment, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
||||||
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
use fragment::{FragmentBoundsIterator, ScannedTextFragment, ScannedTextFragmentInfo};
|
||||||
|
use fragment::SplitInfo;
|
||||||
use incremental::{Reflow, ReflowOutOfFlow};
|
use incremental::{Reflow, ReflowOutOfFlow};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::IntrinsicISizesContribution;
|
use model::IntrinsicISizesContribution;
|
||||||
|
@ -1195,15 +1196,10 @@ impl Flow for InlineFlow {
|
||||||
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
|
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
|
||||||
|
|
||||||
for fragment in self.fragments.fragments.iter_mut() {
|
for fragment in self.fragments.fragments.iter_mut() {
|
||||||
let rel_offset = fragment.relative_position(&self.base
|
let fragment_origin = self.base.child_fragment_absolute_position(fragment);
|
||||||
.absolute_position_info
|
|
||||||
.relative_containing_block_size);
|
|
||||||
let fragment_position = self.base
|
|
||||||
.abs_position
|
|
||||||
.add_size(&rel_offset.to_physical(self.base.writing_mode));
|
|
||||||
fragment.build_display_list(&mut self.base.display_list,
|
fragment.build_display_list(&mut self.base.display_list,
|
||||||
layout_context,
|
layout_context,
|
||||||
fragment_position,
|
fragment_origin,
|
||||||
ContentLevel,
|
ContentLevel,
|
||||||
&self.base.clip_rect);
|
&self.base.clip_rect);
|
||||||
match fragment.specific {
|
match fragment.specific {
|
||||||
|
@ -1223,6 +1219,15 @@ impl Flow for InlineFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
|
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
for fragment in self.fragments.fragments.iter() {
|
||||||
|
if iterator.should_process(fragment) {
|
||||||
|
let fragment_origin = self.base.child_fragment_absolute_position(fragment);
|
||||||
|
iterator.process(fragment, fragment.abs_bounds_from_origin(&fragment_origin));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for InlineFlow {
|
impl fmt::Show for InlineFlow {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use construct::FlowConstructionResult;
|
||||||
use context::SharedLayoutContext;
|
use context::SharedLayoutContext;
|
||||||
use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
use flow::{mod, Flow, ImmutableFlowUtils, MutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use incremental::{LayoutDamageComputation, Reflow, ReflowEntireDocument, Repaint};
|
use incremental::{LayoutDamageComputation, Reflow, ReflowEntireDocument, Repaint};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use parallel::UnsafeFlow;
|
use parallel::UnsafeFlow;
|
||||||
|
@ -24,7 +25,7 @@ use encoding::all::UTF_8;
|
||||||
use geom::point::Point2D;
|
use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayItemIterator, DisplayList};
|
use gfx::display_list::{ContentStackingLevel, DisplayItem, DisplayList};
|
||||||
use gfx::display_list::{OpaqueNode};
|
use gfx::display_list::{OpaqueNode};
|
||||||
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
|
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
|
||||||
use gfx::{render_task, color};
|
use gfx::{render_task, color};
|
||||||
|
@ -34,12 +35,12 @@ use log;
|
||||||
use script::dom::bindings::js::JS;
|
use script::dom::bindings::js::JS;
|
||||||
use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node};
|
use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node};
|
||||||
use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
|
use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
|
||||||
use script::layout_interface::{AddStylesheetMsg, LoadStylesheetMsg, ScriptLayoutChan};
|
use script::layout_interface::{
|
||||||
use script::layout_interface::{TrustedNodeAddress, ContentBoxesResponse, ExitNowMsg};
|
AddStylesheetMsg, ContentBoxResponse, ContentBoxesResponse, ContentBoxesQuery,
|
||||||
use script::layout_interface::{ContentBoxResponse, HitTestResponse, MouseOverResponse};
|
ContentBoxQuery, ExitNowMsg, GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC,
|
||||||
use script::layout_interface::{LayoutChan, Msg, PrepareToExitMsg};
|
LoadStylesheetMsg, MouseOverResponse, Msg, NoQuery, PrepareToExitMsg, ReapLayoutDataMsg,
|
||||||
use script::layout_interface::{GetRPCMsg, LayoutRPC, ReapLayoutDataMsg, Reflow};
|
Reflow, ReflowForDisplay, ReflowMsg, ScriptLayoutChan, TrustedNodeAddress,
|
||||||
use script::layout_interface::{ReflowForDisplay, ReflowMsg};
|
};
|
||||||
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
|
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
|
||||||
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
|
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
|
||||||
use servo_msg::compositor_msg::Scrollable;
|
use servo_msg::compositor_msg::Scrollable;
|
||||||
|
@ -95,6 +96,12 @@ pub struct LayoutTaskData {
|
||||||
/// True if a style sheet was added since the last reflow. Currently, this causes all nodes to
|
/// True if a style sheet was added since the last reflow. Currently, this causes all nodes to
|
||||||
/// be dirtied at the next reflow.
|
/// be dirtied at the next reflow.
|
||||||
pub stylesheet_dirty: bool,
|
pub stylesheet_dirty: bool,
|
||||||
|
|
||||||
|
/// A queued response for the union of the content boxes of a node.
|
||||||
|
pub content_box_response: Rect<Au>,
|
||||||
|
|
||||||
|
/// A queued response for the content boxes of a node.
|
||||||
|
pub content_boxes_response: Vec<Rect<Au>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Information needed by the layout task.
|
/// Information needed by the layout task.
|
||||||
|
@ -284,6 +291,8 @@ impl LayoutTask {
|
||||||
dirty: Rect::zero(),
|
dirty: Rect::zero(),
|
||||||
generation: 0,
|
generation: 0,
|
||||||
stylesheet_dirty: false,
|
stylesheet_dirty: false,
|
||||||
|
content_box_response: Rect::zero(),
|
||||||
|
content_boxes_response: Vec::new(),
|
||||||
})),
|
})),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -587,6 +596,124 @@ impl LayoutTask {
|
||||||
fn verify_flow_tree(&self, _: &mut FlowRef) {
|
fn verify_flow_tree(&self, _: &mut FlowRef) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn process_content_box_request<'a>(&'a self,
|
||||||
|
requested_node: TrustedNodeAddress,
|
||||||
|
layout_root: &mut FlowRef,
|
||||||
|
rw_data: &mut RWGuard<'a>) {
|
||||||
|
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
|
||||||
|
let mut iterator = UnioningFragmentBoundsIterator::new(requested_node);
|
||||||
|
sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator);
|
||||||
|
rw_data.content_box_response = iterator.rect;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_content_boxes_request<'a>(&'a self,
|
||||||
|
requested_node: TrustedNodeAddress,
|
||||||
|
layout_root: &mut FlowRef,
|
||||||
|
rw_data: &mut RWGuard<'a>) {
|
||||||
|
let requested_node: OpaqueNode = OpaqueNodeMethods::from_script_node(requested_node);
|
||||||
|
let mut iterator = CollectingFragmentBoundsIterator::new(requested_node);
|
||||||
|
sequential::iterate_through_flow_tree_fragment_bounds(layout_root, &mut iterator);
|
||||||
|
rw_data.content_boxes_response = iterator.rects;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_display_list_for_reflow<'a>(&'a self,
|
||||||
|
data: &Reflow,
|
||||||
|
node: &mut LayoutNode,
|
||||||
|
layout_root: &mut FlowRef,
|
||||||
|
shared_layout_ctx: &mut SharedLayoutContext,
|
||||||
|
rw_data: &mut RWGuard<'a>) {
|
||||||
|
let writing_mode = flow::base(layout_root.deref()).writing_mode;
|
||||||
|
profile(time::LayoutDispListBuildCategory,
|
||||||
|
Some((&data.url, data.iframe, self.first_reflow.get())),
|
||||||
|
self.time_profiler_chan.clone(),
|
||||||
|
|| {
|
||||||
|
shared_layout_ctx.dirty =
|
||||||
|
flow::base(layout_root.deref()).position.to_physical(writing_mode,
|
||||||
|
rw_data.screen_size);
|
||||||
|
flow::mut_base(layout_root.deref_mut()).abs_position =
|
||||||
|
LogicalPoint::zero(writing_mode).to_physical(writing_mode,
|
||||||
|
rw_data.screen_size);
|
||||||
|
|
||||||
|
let rw_data = rw_data.deref_mut();
|
||||||
|
match rw_data.parallel_traversal {
|
||||||
|
None => {
|
||||||
|
sequential::build_display_list_for_subtree(layout_root, shared_layout_ctx);
|
||||||
|
}
|
||||||
|
Some(ref mut traversal) => {
|
||||||
|
parallel::build_display_list_for_subtree(layout_root,
|
||||||
|
&data.url,
|
||||||
|
data.iframe,
|
||||||
|
self.first_reflow.get(),
|
||||||
|
self.time_profiler_chan.clone(),
|
||||||
|
shared_layout_ctx,
|
||||||
|
traversal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Done building display list. Display List = {}",
|
||||||
|
flow::base(layout_root.deref()).display_list);
|
||||||
|
|
||||||
|
flow::mut_base(layout_root.deref_mut()).display_list.flatten(ContentStackingLevel);
|
||||||
|
let display_list =
|
||||||
|
Arc::new(mem::replace(&mut flow::mut_base(layout_root.deref_mut()).display_list,
|
||||||
|
DisplayList::new()));
|
||||||
|
|
||||||
|
// FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
|
||||||
|
// it with extreme prejudice.
|
||||||
|
let mut color = color::rgba(1.0, 1.0, 1.0, 1.0);
|
||||||
|
for child in node.traverse_preorder() {
|
||||||
|
if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) ||
|
||||||
|
child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) {
|
||||||
|
let element_bg_color = {
|
||||||
|
let thread_safe_child = ThreadSafeLayoutNode::new(&child);
|
||||||
|
thread_safe_child.style()
|
||||||
|
.resolve_color(thread_safe_child.style()
|
||||||
|
.get_background()
|
||||||
|
.background_color)
|
||||||
|
.to_gfx_color()
|
||||||
|
};
|
||||||
|
match element_bg_color {
|
||||||
|
color::rgba(0., 0., 0., 0.) => {}
|
||||||
|
_ => {
|
||||||
|
color = element_bg_color;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let root_size = {
|
||||||
|
let root_flow = flow::base(layout_root.deref());
|
||||||
|
root_flow.position.size.to_physical(root_flow.writing_mode)
|
||||||
|
};
|
||||||
|
let root_size = Size2D(root_size.width.to_nearest_px() as uint,
|
||||||
|
root_size.height.to_nearest_px() as uint);
|
||||||
|
let render_layer = RenderLayer {
|
||||||
|
id: layout_root.layer_id(0),
|
||||||
|
display_list: display_list.clone(),
|
||||||
|
position: Rect(Point2D(0u, 0u), root_size),
|
||||||
|
background_color: color,
|
||||||
|
scroll_policy: Scrollable,
|
||||||
|
};
|
||||||
|
|
||||||
|
rw_data.display_list = Some(display_list);
|
||||||
|
|
||||||
|
// TODO(pcwalton): Eventually, when we have incremental reflow, this will have to
|
||||||
|
// be smarter in order to handle retained layer contents properly from reflow to
|
||||||
|
// reflow.
|
||||||
|
let mut layers = SmallVec1::new();
|
||||||
|
layers.push(render_layer);
|
||||||
|
for layer in mem::replace(&mut flow::mut_base(layout_root.deref_mut()).layers,
|
||||||
|
DList::new()).into_iter() {
|
||||||
|
layers.push(layer)
|
||||||
|
}
|
||||||
|
|
||||||
|
debug!("Layout done!");
|
||||||
|
|
||||||
|
self.render_chan.send(RenderInitMsg(layers));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// The high-level routine that performs layout tasks.
|
/// The high-level routine that performs layout tasks.
|
||||||
fn handle_reflow<'a>(&'a self,
|
fn handle_reflow<'a>(&'a self,
|
||||||
data: &Reflow,
|
data: &Reflow,
|
||||||
|
@ -713,97 +840,19 @@ impl LayoutTask {
|
||||||
|
|
||||||
// Build the display list if necessary, and send it to the renderer.
|
// Build the display list if necessary, and send it to the renderer.
|
||||||
if data.goal == ReflowForDisplay {
|
if data.goal == ReflowForDisplay {
|
||||||
let writing_mode = flow::base(layout_root.deref()).writing_mode;
|
self.build_display_list_for_reflow(data,
|
||||||
profile(time::LayoutDispListBuildCategory,
|
node,
|
||||||
Some((&data.url, data.iframe, self.first_reflow.get())),
|
&mut layout_root,
|
||||||
self.time_profiler_chan.clone(),
|
&mut shared_layout_ctx,
|
||||||
|| {
|
&mut rw_data);
|
||||||
shared_layout_ctx.dirty =
|
}
|
||||||
flow::base(layout_root.deref()).position.to_physical(writing_mode,
|
|
||||||
rw_data.screen_size);
|
|
||||||
flow::mut_base(layout_root.deref_mut()).abs_position =
|
|
||||||
LogicalPoint::zero(writing_mode).to_physical(writing_mode,
|
|
||||||
rw_data.screen_size);
|
|
||||||
|
|
||||||
let rw_data = rw_data.deref_mut();
|
match data.query_type {
|
||||||
match rw_data.parallel_traversal {
|
ContentBoxQuery(node) =>
|
||||||
None => {
|
self.process_content_box_request(node, &mut layout_root, &mut rw_data),
|
||||||
sequential::build_display_list_for_subtree(&mut layout_root,
|
ContentBoxesQuery(node) =>
|
||||||
&shared_layout_ctx);
|
self.process_content_boxes_request(node, &mut layout_root, &mut rw_data),
|
||||||
}
|
NoQuery => {},
|
||||||
Some(ref mut traversal) => {
|
|
||||||
parallel::build_display_list_for_subtree(&mut layout_root,
|
|
||||||
&data.url,
|
|
||||||
data.iframe,
|
|
||||||
self.first_reflow.get(),
|
|
||||||
self.time_profiler_chan.clone(),
|
|
||||||
&shared_layout_ctx,
|
|
||||||
traversal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Done building display list. Display List = {}",
|
|
||||||
flow::base(layout_root.deref()).display_list);
|
|
||||||
|
|
||||||
flow::mut_base(&mut *layout_root).display_list.flatten(ContentStackingLevel);
|
|
||||||
let display_list =
|
|
||||||
Arc::new(mem::replace(&mut flow::mut_base(&mut *layout_root).display_list,
|
|
||||||
DisplayList::new()));
|
|
||||||
|
|
||||||
// FIXME(pcwalton): This is really ugly and can't handle overflow: scroll. Refactor
|
|
||||||
// it with extreme prejudice.
|
|
||||||
let mut color = color::rgba(1.0, 1.0, 1.0, 1.0);
|
|
||||||
for child in node.traverse_preorder() {
|
|
||||||
if child.type_id() == Some(ElementNodeTypeId(HTMLHtmlElementTypeId)) ||
|
|
||||||
child.type_id() == Some(ElementNodeTypeId(HTMLBodyElementTypeId)) {
|
|
||||||
let element_bg_color = {
|
|
||||||
let thread_safe_child = ThreadSafeLayoutNode::new(&child);
|
|
||||||
thread_safe_child.style()
|
|
||||||
.resolve_color(thread_safe_child.style()
|
|
||||||
.get_background()
|
|
||||||
.background_color)
|
|
||||||
.to_gfx_color()
|
|
||||||
};
|
|
||||||
match element_bg_color {
|
|
||||||
color::rgba(0., 0., 0., 0.) => {}
|
|
||||||
_ => {
|
|
||||||
color = element_bg_color;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let root_size = {
|
|
||||||
let root_flow = flow::base(layout_root.deref());
|
|
||||||
root_flow.position.size.to_physical(root_flow.writing_mode)
|
|
||||||
};
|
|
||||||
let root_size = Size2D(root_size.width.to_nearest_px() as uint,
|
|
||||||
root_size.height.to_nearest_px() as uint);
|
|
||||||
let render_layer = RenderLayer {
|
|
||||||
id: layout_root.layer_id(0),
|
|
||||||
display_list: display_list.clone(),
|
|
||||||
position: Rect(Point2D(0u, 0u), root_size),
|
|
||||||
background_color: color,
|
|
||||||
scroll_policy: Scrollable,
|
|
||||||
};
|
|
||||||
|
|
||||||
rw_data.display_list = Some(display_list);
|
|
||||||
|
|
||||||
// TODO(pcwalton): Eventually, when we have incremental reflow, this will have to
|
|
||||||
// be smarter in order to handle retained layer contents properly from reflow to
|
|
||||||
// reflow.
|
|
||||||
let mut layers = SmallVec1::new();
|
|
||||||
layers.push(render_layer);
|
|
||||||
for layer in mem::replace(&mut flow::mut_base(layout_root.deref_mut()).layers,
|
|
||||||
DList::new()).into_iter() {
|
|
||||||
layers.push(layer)
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!("Layout done!");
|
|
||||||
|
|
||||||
self.render_chan.send(RenderInitMsg(layers));
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.first_reflow.set(false);
|
self.first_reflow.set(false);
|
||||||
|
@ -876,61 +925,17 @@ struct LayoutRPCImpl(Arc<Mutex<LayoutTaskData>>);
|
||||||
impl LayoutRPC for LayoutRPCImpl {
|
impl LayoutRPC for LayoutRPCImpl {
|
||||||
// The neat thing here is that in order to answer the following two queries we only
|
// The neat thing here is that in order to answer the following two queries we only
|
||||||
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
|
// need to compare nodes for equality. Thus we can safely work only with `OpaqueNode`.
|
||||||
fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse {
|
fn content_box(&self) -> ContentBoxResponse {
|
||||||
let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
fn union_boxes_for_node(accumulator: &mut Option<Rect<Au>>,
|
let rw_data = rw_data.lock();
|
||||||
mut iter: DisplayItemIterator,
|
ContentBoxResponse(rw_data.content_box_response)
|
||||||
node: OpaqueNode) {
|
|
||||||
for item in iter {
|
|
||||||
if item.base().node == node {
|
|
||||||
match *accumulator {
|
|
||||||
None => *accumulator = Some(item.base().bounds),
|
|
||||||
Some(ref mut acc) => *acc = acc.union(&item.base().bounds),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut rect = None;
|
|
||||||
{
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock();
|
|
||||||
match rw_data.display_list {
|
|
||||||
None => fail!("no display list!"),
|
|
||||||
Some(ref display_list) => {
|
|
||||||
union_boxes_for_node(&mut rect, display_list.iter(), node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentBoxResponse(rect.unwrap_or(Rect::zero()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
|
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
|
||||||
fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse {
|
fn content_boxes(&self) -> ContentBoxesResponse {
|
||||||
let node: OpaqueNode = OpaqueNodeMethods::from_script_node(node);
|
let &LayoutRPCImpl(ref rw_data) = self;
|
||||||
|
let mut rw_data = rw_data.lock();
|
||||||
fn add_boxes_for_node(accumulator: &mut Vec<Rect<Au>>,
|
ContentBoxesResponse(rw_data.content_boxes_response.clone())
|
||||||
mut iter: DisplayItemIterator,
|
|
||||||
node: OpaqueNode) {
|
|
||||||
for item in iter {
|
|
||||||
if item.base().node == node {
|
|
||||||
accumulator.push(item.base().bounds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut boxes = vec!();
|
|
||||||
{
|
|
||||||
let &LayoutRPCImpl(ref rw_data) = self;
|
|
||||||
let rw_data = rw_data.lock();
|
|
||||||
match rw_data.display_list {
|
|
||||||
None => fail!("no display list!"),
|
|
||||||
Some(ref display_list) => {
|
|
||||||
add_boxes_for_node(&mut boxes, display_list.iter(), node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ContentBoxesResponse(boxes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Requests the node containing the point of interest
|
/// Requests the node containing the point of interest
|
||||||
|
@ -997,3 +1002,55 @@ impl LayoutRPC for LayoutRPCImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UnioningFragmentBoundsIterator {
|
||||||
|
node_address: OpaqueNode,
|
||||||
|
rect: Rect<Au>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnioningFragmentBoundsIterator {
|
||||||
|
fn new(node_address: OpaqueNode) -> UnioningFragmentBoundsIterator {
|
||||||
|
UnioningFragmentBoundsIterator {
|
||||||
|
node_address: node_address,
|
||||||
|
rect: Rect::zero(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FragmentBoundsIterator for UnioningFragmentBoundsIterator {
|
||||||
|
fn process(&mut self, _: &Fragment, bounds: Rect<Au>) {
|
||||||
|
if self.rect.is_empty() {
|
||||||
|
self.rect = bounds;
|
||||||
|
} else {
|
||||||
|
self.rect = self.rect.union(&bounds);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
||||||
|
self.node_address == fragment.node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CollectingFragmentBoundsIterator {
|
||||||
|
node_address: OpaqueNode,
|
||||||
|
rects: Vec<Rect<Au>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CollectingFragmentBoundsIterator {
|
||||||
|
fn new(node_address: OpaqueNode) -> CollectingFragmentBoundsIterator {
|
||||||
|
CollectingFragmentBoundsIterator {
|
||||||
|
node_address: node_address,
|
||||||
|
rects: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FragmentBoundsIterator for CollectingFragmentBoundsIterator {
|
||||||
|
fn process(&mut self, _: &Fragment, bounds: Rect<Au>) {
|
||||||
|
self.rects.push(bounds);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn should_process(&mut self, fragment: &Fragment) -> bool {
|
||||||
|
self.node_address == fragment.node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ use context::{LayoutContext, SharedLayoutContext};
|
||||||
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
|
use flow::{Flow, MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal};
|
||||||
use flow;
|
use flow;
|
||||||
use flow_ref::FlowRef;
|
use flow_ref::FlowRef;
|
||||||
|
use fragment::FragmentBoundsIterator;
|
||||||
use servo_util::opts;
|
use servo_util::opts;
|
||||||
use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows};
|
use traversal::{BubbleISizes, RecalcStyleForNode, ConstructFlows};
|
||||||
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes};
|
use traversal::{AssignBSizesAndStoreOverflow, AssignISizes};
|
||||||
|
@ -92,3 +93,16 @@ pub fn build_display_list_for_subtree(root: &mut FlowRef,
|
||||||
|
|
||||||
doit(root.deref_mut(), compute_absolute_positions, build_display_list);
|
doit(root.deref_mut(), compute_absolute_positions, build_display_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn iterate_through_flow_tree_fragment_bounds(root: &mut FlowRef,
|
||||||
|
iterator: &mut FragmentBoundsIterator) {
|
||||||
|
fn doit(flow: &mut Flow, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
flow.iterate_through_fragment_bounds(iterator);
|
||||||
|
|
||||||
|
for kid in flow::mut_base(flow).child_iter() {
|
||||||
|
doit(kid, iterator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doit(root.deref_mut(), iterator);
|
||||||
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use fragment::Fragment;
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::{IntrinsicISizes, IntrinsicISizesContribution};
|
use model::{IntrinsicISizes, IntrinsicISizesContribution};
|
||||||
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
use table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
||||||
|
@ -327,6 +327,10 @@ impl Flow for TableFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.block_flow.repair_style(new_style)
|
self.block_flow.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
self.block_flow.iterate_through_fragment_bounds(iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableFlow {
|
impl fmt::Show for TableFlow {
|
||||||
|
|
|
@ -10,6 +10,7 @@ use block::BlockFlow;
|
||||||
use construct::FlowConstructor;
|
use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{TableCaptionFlowClass, FlowClass, Flow};
|
use flow::{TableCaptionFlowClass, FlowClass, Flow};
|
||||||
|
use fragment::FragmentBoundsIterator;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
@ -79,6 +80,10 @@ impl Flow for TableCaptionFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.block_flow.repair_style(new_style)
|
self.block_flow.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
self.iterate_through_fragment_bounds(iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableCaptionFlow {
|
impl fmt::Show for TableCaptionFlow {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
|
use block::{BlockFlow, MarginsMayNotCollapse, ISizeAndMarginsComputer};
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{TableCellFlowClass, FlowClass, Flow};
|
use flow::{TableCellFlowClass, FlowClass, Flow};
|
||||||
use fragment::Fragment;
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use model::{MaybeAuto};
|
use model::{MaybeAuto};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use table::InternalTable;
|
use table::InternalTable;
|
||||||
|
@ -148,6 +148,10 @@ impl Flow for TableCellFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.block_flow.repair_style(new_style)
|
self.block_flow.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
self.block_flow.iterate_through_fragment_bounds(iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableCellFlow {
|
impl fmt::Show for TableCellFlow {
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
|
use flow::{BaseFlow, TableColGroupFlowClass, FlowClass, Flow};
|
||||||
use fragment::{Fragment, TableColumnFragment};
|
use fragment::{Fragment, FragmentBoundsIterator, TableColumnFragment};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
|
@ -95,6 +95,9 @@ impl Flow for TableColGroupFlow {
|
||||||
fn build_display_list(&mut self, _: &LayoutContext) {}
|
fn build_display_list(&mut self, _: &LayoutContext) {}
|
||||||
|
|
||||||
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
|
fn repair_style(&mut self, _: &Arc<ComputedValues>) {}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, _: &mut FragmentBoundsIterator) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableColGroupFlow {
|
impl fmt::Show for TableColGroupFlow {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::Fragment;
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use table::{ColumnInlineSize, InternalTable};
|
use table::{ColumnInlineSize, InternalTable};
|
||||||
use model::{MaybeAuto, Specified, Auto};
|
use model::{MaybeAuto, Specified, Auto};
|
||||||
|
@ -251,6 +251,10 @@ impl Flow for TableRowFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.block_flow.repair_style(new_style)
|
self.block_flow.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
self.block_flow.iterate_through_fragment_bounds(iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableRowFlow {
|
impl fmt::Show for TableRowFlow {
|
||||||
|
|
|
@ -12,7 +12,7 @@ use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use flow;
|
use flow;
|
||||||
use fragment::Fragment;
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use layout_debug;
|
use layout_debug;
|
||||||
use model::IntrinsicISizesContribution;
|
use model::IntrinsicISizesContribution;
|
||||||
use table::{ColumnInlineSize, InternalTable, TableFlow};
|
use table::{ColumnInlineSize, InternalTable, TableFlow};
|
||||||
|
@ -211,6 +211,10 @@ impl Flow for TableRowGroupFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.block_flow.repair_style(new_style)
|
self.block_flow.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
self.block_flow.iterate_through_fragment_bounds(iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableRowGroupFlow {
|
impl fmt::Show for TableRowGroupFlow {
|
||||||
|
|
|
@ -19,7 +19,7 @@ use construct::FlowConstructor;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use floats::FloatKind;
|
use floats::FloatKind;
|
||||||
use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use fragment::Fragment;
|
use fragment::{Fragment, FragmentBoundsIterator};
|
||||||
use table::ColumnInlineSize;
|
use table::ColumnInlineSize;
|
||||||
use wrapper::ThreadSafeLayoutNode;
|
use wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
|
@ -334,6 +334,10 @@ impl Flow for TableWrapperFlow {
|
||||||
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
fn repair_style(&mut self, new_style: &Arc<ComputedValues>) {
|
||||||
self.block_flow.repair_style(new_style)
|
self.block_flow.repair_style(new_style)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn iterate_through_fragment_bounds(&self, iterator: &mut FragmentBoundsIterator) {
|
||||||
|
self.block_flow.iterate_through_fragment_bounds(iterator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Show for TableWrapperFlow {
|
impl fmt::Show for TableWrapperFlow {
|
||||||
|
|
|
@ -45,8 +45,7 @@ use dom::text::Text;
|
||||||
use dom::virtualmethods::{VirtualMethods, vtable_for};
|
use dom::virtualmethods::{VirtualMethods, vtable_for};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use layout_interface::{ContentBoxResponse, ContentBoxesResponse, LayoutRPC,
|
use layout_interface::{LayoutChan, ReapLayoutDataMsg};
|
||||||
LayoutChan, ReapLayoutDataMsg};
|
|
||||||
use devtools_traits::NodeInfo;
|
use devtools_traits::NodeInfo;
|
||||||
use script_traits::UntrustedNodeAddress;
|
use script_traits::UntrustedNodeAddress;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
@ -689,20 +688,11 @@ impl<'a> NodeHelpers<'a> for JSRef<'a, Node> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bounding_content_box(self) -> Rect<Au> {
|
fn get_bounding_content_box(self) -> Rect<Au> {
|
||||||
let window = window_from_node(self).root();
|
window_from_node(self).root().page().content_box_query(self.to_trusted_node_address())
|
||||||
let page = window.page();
|
|
||||||
let addr = self.to_trusted_node_address();
|
|
||||||
|
|
||||||
let ContentBoxResponse(rect) = page.layout().content_box(addr);
|
|
||||||
rect
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_content_boxes(self) -> Vec<Rect<Au>> {
|
fn get_content_boxes(self) -> Vec<Rect<Au>> {
|
||||||
let window = window_from_node(self).root();
|
window_from_node(self).root().page().content_boxes_query(self.to_trusted_node_address())
|
||||||
let page = window.page();
|
|
||||||
let addr = self.to_trusted_node_address();
|
|
||||||
let ContentBoxesResponse(rects) = page.layout().content_boxes(addr);
|
|
||||||
rects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
|
// http://dom.spec.whatwg.org/#dom-parentnode-queryselector
|
||||||
|
|
|
@ -19,7 +19,7 @@ use dom::location::Location;
|
||||||
use dom::navigator::Navigator;
|
use dom::navigator::Navigator;
|
||||||
use dom::performance::Performance;
|
use dom::performance::Performance;
|
||||||
use dom::screen::Screen;
|
use dom::screen::Screen;
|
||||||
use layout_interface::ReflowGoal;
|
use layout_interface::NoQuery;
|
||||||
use page::Page;
|
use page::Page;
|
||||||
use script_task::{ExitWindowMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg};
|
use script_task::{ExitWindowMsg, ScriptChan, TriggerLoadMsg, TriggerFragmentMsg};
|
||||||
use script_task::FromWindow;
|
use script_task::FromWindow;
|
||||||
|
@ -312,7 +312,7 @@ impl Reflectable for Window {
|
||||||
|
|
||||||
pub trait WindowHelpers {
|
pub trait WindowHelpers {
|
||||||
fn reflow(self);
|
fn reflow(self);
|
||||||
fn flush_layout(self, goal: ReflowGoal);
|
fn flush_layout(self);
|
||||||
fn wait_until_safe_to_modify_dom(self);
|
fn wait_until_safe_to_modify_dom(self);
|
||||||
fn init_browser_context(self, doc: JSRef<Document>);
|
fn init_browser_context(self, doc: JSRef<Document>);
|
||||||
fn load_url(self, href: DOMString);
|
fn load_url(self, href: DOMString);
|
||||||
|
@ -350,8 +350,8 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
||||||
self.page().damage();
|
self.page().damage();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flush_layout(self, goal: ReflowGoal) {
|
fn flush_layout(self) {
|
||||||
self.page().flush_layout(goal);
|
self.page().flush_layout(NoQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait_until_safe_to_modify_dom(self) {
|
fn wait_until_safe_to_modify_dom(self) {
|
||||||
|
|
|
@ -60,9 +60,9 @@ pub enum Msg {
|
||||||
// 3) and really needs to be fast.
|
// 3) and really needs to be fast.
|
||||||
pub trait LayoutRPC {
|
pub trait LayoutRPC {
|
||||||
/// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
|
/// Requests the dimensions of the content box, as in the `getBoundingClientRect()` call.
|
||||||
fn content_box(&self, node: TrustedNodeAddress) -> ContentBoxResponse;
|
fn content_box(&self) -> ContentBoxResponse;
|
||||||
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
|
/// Requests the dimensions of all the content boxes, as in the `getClientRects()` call.
|
||||||
fn content_boxes(&self, node: TrustedNodeAddress) -> ContentBoxesResponse;
|
fn content_boxes(&self) -> ContentBoxesResponse;
|
||||||
/// Requests the node containing the point of interest
|
/// Requests the node containing the point of interest
|
||||||
fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>;
|
fn hit_test(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<HitTestResponse, ()>;
|
||||||
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
|
fn mouse_over(&self, node: TrustedNodeAddress, point: Point2D<f32>) -> Result<MouseOverResponse, ()>;
|
||||||
|
@ -82,6 +82,13 @@ pub enum ReflowGoal {
|
||||||
ReflowForScriptQuery,
|
ReflowForScriptQuery,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Any query to perform with this reflow.
|
||||||
|
pub enum ReflowQueryType {
|
||||||
|
NoQuery,
|
||||||
|
ContentBoxQuery(TrustedNodeAddress),
|
||||||
|
ContentBoxesQuery(TrustedNodeAddress),
|
||||||
|
}
|
||||||
|
|
||||||
/// Information needed for a reflow.
|
/// Information needed for a reflow.
|
||||||
pub struct Reflow {
|
pub struct Reflow {
|
||||||
/// The document node.
|
/// The document node.
|
||||||
|
@ -99,7 +106,9 @@ pub struct Reflow {
|
||||||
/// The channel that we send a notification to.
|
/// The channel that we send a notification to.
|
||||||
pub script_join_chan: Sender<()>,
|
pub script_join_chan: Sender<()>,
|
||||||
/// Unique identifier
|
/// Unique identifier
|
||||||
pub id: uint
|
pub id: uint,
|
||||||
|
/// The type of query if any to perform during this reflow.
|
||||||
|
pub query_type: ReflowQueryType,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encapsulates a channel to the layout task.
|
/// Encapsulates a channel to the layout task.
|
||||||
|
|
|
@ -11,19 +11,22 @@ use dom::document::{Document, DocumentHelpers};
|
||||||
use dom::element::Element;
|
use dom::element::Element;
|
||||||
use dom::node::{Node, NodeHelpers};
|
use dom::node::{Node, NodeHelpers};
|
||||||
use dom::window::Window;
|
use dom::window::Window;
|
||||||
use layout_interface::{ReflowForDisplay};
|
use layout_interface::{
|
||||||
use layout_interface::{HitTestResponse, MouseOverResponse};
|
ContentBoxQuery, ContentBoxResponse, ContentBoxesQuery, ContentBoxesResponse,
|
||||||
use layout_interface::{GetRPCMsg, LayoutChan, LayoutRPC};
|
GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC, MouseOverResponse, NoQuery,
|
||||||
use layout_interface::{Reflow, ReflowGoal, ReflowMsg};
|
Reflow, ReflowForDisplay, ReflowForScriptQuery, ReflowGoal, ReflowMsg,
|
||||||
|
ReflowQueryType, TrustedNodeAddress
|
||||||
|
};
|
||||||
use script_traits::{UntrustedNodeAddress, ScriptControlChan};
|
use script_traits::{UntrustedNodeAddress, ScriptControlChan};
|
||||||
|
|
||||||
use geom::point::Point2D;
|
use geom::{Point2D, Rect};
|
||||||
use js::rust::Cx;
|
use js::rust::Cx;
|
||||||
use servo_msg::compositor_msg::PerformingLayout;
|
use servo_msg::compositor_msg::PerformingLayout;
|
||||||
use servo_msg::compositor_msg::ScriptListener;
|
use servo_msg::compositor_msg::ScriptListener;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
|
use servo_msg::constellation_msg::{ConstellationChan, WindowSizeData};
|
||||||
use servo_msg::constellation_msg::{PipelineId, SubpageId};
|
use servo_msg::constellation_msg::{PipelineId, SubpageId};
|
||||||
use servo_net::resource_task::ResourceTask;
|
use servo_net::resource_task::ResourceTask;
|
||||||
|
use servo_util::geometry::Au;
|
||||||
use servo_util::str::DOMString;
|
use servo_util::str::DOMString;
|
||||||
use servo_util::smallvec::{SmallVec1, SmallVec};
|
use servo_util::smallvec::{SmallVec1, SmallVec};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
|
@ -164,26 +167,48 @@ impl Page {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flush_layout(&self, goal: ReflowGoal) {
|
pub fn flush_layout(&self, query: ReflowQueryType) {
|
||||||
if self.damaged.get() {
|
// If we are damaged, we need to force a full reflow, so that queries interact with
|
||||||
|
// an accurate flow tree.
|
||||||
|
let (reflow_goal, force_reflow) = if self.damaged.get() {
|
||||||
|
(ReflowForDisplay, true)
|
||||||
|
} else {
|
||||||
|
match query {
|
||||||
|
ContentBoxQuery(_) | ContentBoxesQuery(_) => (ReflowForScriptQuery, true),
|
||||||
|
NoQuery => (ReflowForDisplay, false),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if force_reflow {
|
||||||
let frame = self.frame();
|
let frame = self.frame();
|
||||||
let window = frame.as_ref().unwrap().window.root();
|
let window = frame.as_ref().unwrap().window.root();
|
||||||
self.reflow(goal, window.control_chan().clone(), window.compositor());
|
self.reflow(reflow_goal, window.control_chan().clone(), window.compositor(), query);
|
||||||
} else {
|
} else {
|
||||||
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
|
self.avoided_reflows.set(self.avoided_reflows.get() + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout(&self) -> &LayoutRPC {
|
pub fn layout(&self) -> &LayoutRPC {
|
||||||
// FIXME This should probably be ReflowForQuery, not Display. All queries currently
|
self.flush_layout(NoQuery);
|
||||||
// currently rely on the display list, which means we can't destroy it by
|
|
||||||
// doing a query reflow.
|
|
||||||
self.flush_layout(ReflowForDisplay);
|
|
||||||
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
||||||
let layout_rpc: &LayoutRPC = &*self.layout_rpc;
|
let layout_rpc: &LayoutRPC = &*self.layout_rpc;
|
||||||
layout_rpc
|
layout_rpc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
|
||||||
|
self.flush_layout(ContentBoxQuery(content_box_request));
|
||||||
|
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
||||||
|
let ContentBoxResponse(rect) = self.layout_rpc.content_box();
|
||||||
|
rect
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
|
||||||
|
self.flush_layout(ContentBoxesQuery(content_boxes_request));
|
||||||
|
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
||||||
|
let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
|
||||||
|
rects
|
||||||
|
}
|
||||||
|
|
||||||
// must handle root case separately
|
// must handle root case separately
|
||||||
pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> {
|
pub fn remove(&self, id: PipelineId) -> Option<Rc<Page>> {
|
||||||
let remove_idx = {
|
let remove_idx = {
|
||||||
|
@ -303,7 +328,8 @@ impl Page {
|
||||||
pub fn reflow(&self,
|
pub fn reflow(&self,
|
||||||
goal: ReflowGoal,
|
goal: ReflowGoal,
|
||||||
script_chan: ScriptControlChan,
|
script_chan: ScriptControlChan,
|
||||||
compositor: &ScriptListener) {
|
compositor: &ScriptListener,
|
||||||
|
query_type: ReflowQueryType) {
|
||||||
|
|
||||||
let root = match *self.frame() {
|
let root = match *self.frame() {
|
||||||
None => return,
|
None => return,
|
||||||
|
@ -349,6 +375,7 @@ impl Page {
|
||||||
script_chan: script_chan,
|
script_chan: script_chan,
|
||||||
script_join_chan: join_chan,
|
script_join_chan: join_chan,
|
||||||
id: last_reflow_id.get(),
|
id: last_reflow_id.get(),
|
||||||
|
query_type: query_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
let LayoutChan(ref chan) = self.layout_chan;
|
let LayoutChan(ref chan) = self.layout_chan;
|
||||||
|
|
|
@ -29,7 +29,7 @@ use dom::window::{Window, WindowHelpers};
|
||||||
use dom::worker::{Worker, TrustedWorkerAddress};
|
use dom::worker::{Worker, TrustedWorkerAddress};
|
||||||
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
|
use dom::xmlhttprequest::{TrustedXHRAddress, XMLHttpRequest, XHRProgress};
|
||||||
use parse::html::{InputString, InputUrl, parse_html};
|
use parse::html::{InputString, InputUrl, parse_html};
|
||||||
use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowForDisplay};
|
use layout_interface::{ScriptLayoutChan, LayoutChan, NoQuery, ReflowForDisplay};
|
||||||
use layout_interface;
|
use layout_interface;
|
||||||
use page::{Page, IterablePage, Frame};
|
use page::{Page, IterablePage, Frame};
|
||||||
use timers::TimerId;
|
use timers::TimerId;
|
||||||
|
@ -815,7 +815,7 @@ impl ScriptTask {
|
||||||
let document_as_node = NodeCast::from_ref(document_js_ref);
|
let document_as_node = NodeCast::from_ref(document_js_ref);
|
||||||
document.content_changed(document_as_node);
|
document.content_changed(document_as_node);
|
||||||
}
|
}
|
||||||
window.flush_layout(ReflowForDisplay);
|
window.flush_layout();
|
||||||
|
|
||||||
{
|
{
|
||||||
// No more reflow required
|
// No more reflow required
|
||||||
|
@ -870,7 +870,7 @@ impl ScriptTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
page.damage();
|
page.damage();
|
||||||
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor);
|
page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor, NoQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the main entry point for receiving and dispatching DOM events.
|
/// This is the main entry point for receiving and dispatching DOM events.
|
||||||
|
@ -969,7 +969,7 @@ impl ScriptTask {
|
||||||
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(node);
|
let eventtarget: JSRef<EventTarget> = EventTargetCast::from_ref(node);
|
||||||
let _ = eventtarget.dispatch_event_with_target(None, *event);
|
let _ = eventtarget.dispatch_event_with_target(None, *event);
|
||||||
|
|
||||||
window.flush_layout(ReflowForDisplay);
|
window.flush_layout();
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue