mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Add a layout debug module. This outputs a trace of the layout process to a JSON
file which can be viewed in an external tool. It provides a timelapse view of how the flow tree and fragments changed during the layout process, which makes it easier to debug layout bugs.
This commit is contained in:
parent
940c013176
commit
acedb16670
16 changed files with 261 additions and 16 deletions
|
@ -66,6 +66,7 @@ pub extern "C" fn cef_run_message_loop() {
|
|||
bubble_inline_sizes_separately: false,
|
||||
show_debug_borders: false,
|
||||
enable_text_antialiasing: true,
|
||||
trace_layout: false,
|
||||
};
|
||||
native::start(0, 0 as *const *const u8, proc() {
|
||||
servo::run(opts);
|
||||
|
|
|
@ -21,6 +21,7 @@ extern crate native;
|
|||
extern crate rustrt;
|
||||
extern crate stb_image;
|
||||
extern crate png;
|
||||
extern crate serialize;
|
||||
#[phase(plugin)]
|
||||
extern crate servo_macros = "macros";
|
||||
extern crate servo_net = "net";
|
||||
|
|
|
@ -511,6 +511,7 @@ pub struct GlyphStore {
|
|||
}
|
||||
|
||||
int_range_index! {
|
||||
#[deriving(Encodable)]
|
||||
#[doc = "An index that refers to a character in a text run. This could \
|
||||
point to the middle of a glyph."]
|
||||
struct CharIndex(int)
|
||||
|
|
|
@ -21,6 +21,7 @@ use flow::{BaseFlow, BlockFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
|||
use flow::{MutableFlowUtils, PreorderFlowTraversal, PostorderFlowTraversal, mut_base};
|
||||
use flow;
|
||||
use fragment::{Fragment, ImageFragment, ScannedTextFragment};
|
||||
use layout_debug;
|
||||
use model::{Auto, IntrinsicISizes, MarginCollapseInfo, MarginsCollapse};
|
||||
use model::{MarginsCollapseThrough, MaybeAuto, NoCollapsibleMargins, Specified, specified};
|
||||
use model::{specified_or_none};
|
||||
|
@ -48,6 +49,7 @@ use style::computed_values::{display, float, overflow};
|
|||
use sync::Arc;
|
||||
|
||||
/// Information specific to floated blocks.
|
||||
#[deriving(Encodable)]
|
||||
pub struct FloatedBlockInfo {
|
||||
pub containing_inline_size: Au,
|
||||
|
||||
|
@ -492,6 +494,7 @@ fn propagate_layer_flag_from_child(layers_needed_for_descendants: &mut bool, kid
|
|||
}
|
||||
|
||||
// A block formatting context.
|
||||
#[deriving(Encodable)]
|
||||
pub struct BlockFlow {
|
||||
/// Data common to all flows.
|
||||
pub base: BaseFlow,
|
||||
|
@ -808,6 +811,8 @@ impl BlockFlow {
|
|||
pub fn assign_block_size_block_base<'a>(&mut self,
|
||||
layout_context: &'a LayoutContext<'a>,
|
||||
margins_may_collapse: MarginsMayCollapseFlag) {
|
||||
let _scope = layout_debug::Scope::new(format!("assign_block_size_block_base {:s}", self.base.debug_id()));
|
||||
|
||||
// Our current border-box position.
|
||||
let mut cur_b = Au(0);
|
||||
|
||||
|
@ -1054,6 +1059,8 @@ impl BlockFlow {
|
|||
///
|
||||
/// It does not calculate the block-size of the flow itself.
|
||||
pub fn assign_block_size_float<'a>(&mut self, ctx: &'a LayoutContext<'a>) {
|
||||
let _scope = layout_debug::Scope::new(format!("assign_block_size_float {:s}", self.base.debug_id()));
|
||||
|
||||
let mut floats = Floats::new(self.fragment.style.writing_mode);
|
||||
for kid in self.base.child_iter() {
|
||||
flow::mut_base(kid).floats = floats.clone();
|
||||
|
@ -1432,6 +1439,10 @@ impl Flow for BlockFlow {
|
|||
self
|
||||
}
|
||||
|
||||
fn as_immutable_block<'a>(&'a self) -> &'a BlockFlow {
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the direction that this flow clears floats in, if any.
|
||||
fn float_clearance(&self) -> clear::T {
|
||||
self.fragment.style().get_box().clear
|
||||
|
@ -1446,6 +1457,8 @@ impl Flow for BlockFlow {
|
|||
///
|
||||
/// TODO(pcwalton): Inline blocks.
|
||||
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
|
||||
let _scope = layout_debug::Scope::new(format!("block::bubble_inline_sizes {:s}", self.base.debug_id()));
|
||||
|
||||
let mut flags = self.base.flags;
|
||||
flags.set_has_left_floated_descendants(false);
|
||||
flags.set_has_right_floated_descendants(false);
|
||||
|
@ -1490,6 +1503,8 @@ impl Flow for BlockFlow {
|
|||
/// Dual fragments consume some inline-size first, and the remainder is assigned to all child (block)
|
||||
/// contexts.
|
||||
fn assign_inline_sizes(&mut self, layout_context: &LayoutContext) {
|
||||
let _scope = layout_debug::Scope::new(format!("block::assign_inline_sizes {:s}", self.base.debug_id()));
|
||||
|
||||
debug!("assign_inline_sizes({}): assigning inline_size for flow",
|
||||
if self.is_float() {
|
||||
"float"
|
||||
|
|
|
@ -11,7 +11,7 @@ use style::computed_values::float;
|
|||
use sync::Arc;
|
||||
|
||||
/// The kind of float: left or right.
|
||||
#[deriving(Clone)]
|
||||
#[deriving(Clone, Encodable)]
|
||||
pub enum FloatKind {
|
||||
FloatLeft,
|
||||
FloatRight
|
||||
|
|
|
@ -49,6 +49,7 @@ use collections::dlist::DList;
|
|||
use geom::Point2D;
|
||||
use gfx::display_list::DisplayList;
|
||||
use gfx::render_task::RenderLayer;
|
||||
use serialize::{Encoder, Encodable};
|
||||
use servo_msg::compositor_msg::LayerId;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::logical_geometry::WritingMode;
|
||||
|
@ -74,6 +75,12 @@ pub trait Flow: fmt::Show + ToString + Share {
|
|||
/// Returns the class of flow that this is.
|
||||
fn class(&self) -> FlowClass;
|
||||
|
||||
/// If this is a block flow, returns the underlying object, borrowed immutably. Fails
|
||||
/// otherwise.
|
||||
fn as_immutable_block<'a>(&'a self) -> &'a BlockFlow {
|
||||
fail!("called as_immutable_block() on a non-block flow")
|
||||
}
|
||||
|
||||
/// If this is a block flow, returns the underlying object. Fails otherwise.
|
||||
fn as_block<'a>(&'a mut self) -> &'a mut BlockFlow {
|
||||
debug!("called as_block() on a flow of type {}", self.class());
|
||||
|
@ -270,6 +277,21 @@ pub trait Flow: fmt::Show + ToString + Share {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a, E, S: Encoder<E>> Encodable<S, E> for &'a Flow {
|
||||
fn encode(&self, e: &mut S) -> Result<(), E> {
|
||||
e.emit_struct("flow", 0, |e| {
|
||||
try!(e.emit_struct_field("class", 0, |e| self.class().encode(e)))
|
||||
e.emit_struct_field("data", 1, |e| {
|
||||
match self.class() {
|
||||
BlockFlowClass => self.as_immutable_block().encode(e),
|
||||
InlineFlowClass => self.as_immutable_inline().encode(e),
|
||||
_ => { Ok(()) } // TODO: Support tables
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Base access
|
||||
|
||||
#[inline(always)]
|
||||
|
@ -381,7 +403,7 @@ pub trait MutableOwnedFlowUtils {
|
|||
fn set_abs_descendants(&mut self, abs_descendants: AbsDescendants);
|
||||
}
|
||||
|
||||
#[deriving(PartialEq, Show)]
|
||||
#[deriving(Encodable, PartialEq, Show)]
|
||||
pub enum FlowClass {
|
||||
BlockFlowClass,
|
||||
InlineFlowClass,
|
||||
|
@ -428,7 +450,7 @@ pub trait PostorderFlowTraversal {
|
|||
}
|
||||
|
||||
/// Flags used in flows, tightly packed to save space.
|
||||
#[deriving(Clone)]
|
||||
#[deriving(Clone, Encodable)]
|
||||
pub struct FlowFlags(pub u8);
|
||||
|
||||
/// The bitmask of flags that represent the `has_left_floated_descendants` and
|
||||
|
@ -595,6 +617,7 @@ pub type DescendantOffsetIter<'a> = Zip<DescendantIter<'a>, MutItems<'a, Au>>;
|
|||
|
||||
/// Information needed to compute absolute (i.e. viewport-relative) flow positions (not to be
|
||||
/// confused with absolutely-positioned flows).
|
||||
#[deriving(Encodable)]
|
||||
pub struct AbsolutePositionInfo {
|
||||
/// The size of the containing block for relatively-positioned descendants.
|
||||
pub relative_containing_block_size: LogicalSize<Au>,
|
||||
|
@ -697,6 +720,26 @@ pub struct BaseFlow {
|
|||
pub writing_mode: WritingMode,
|
||||
}
|
||||
|
||||
impl<E, S: Encoder<E>> Encodable<S, E> for BaseFlow {
|
||||
fn encode(&self, e: &mut S) -> Result<(), E> {
|
||||
e.emit_struct("base", 0, |e| {
|
||||
try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
|
||||
try!(e.emit_struct_field("abs_position", 1, |e| self.abs_position.encode(e)))
|
||||
try!(e.emit_struct_field("intrinsic_inline_sizes", 2, |e| self.intrinsic_inline_sizes.encode(e)))
|
||||
try!(e.emit_struct_field("position", 3, |e| self.position.encode(e)))
|
||||
e.emit_struct_field("children", 4, |e| {
|
||||
e.emit_seq(self.children.len(), |e| {
|
||||
for (i, c) in self.children.iter().enumerate() {
|
||||
try!(e.emit_seq_elt(i, |e| c.encode(e)))
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[unsafe_destructor]
|
||||
impl Drop for BaseFlow {
|
||||
fn drop(&mut self) {
|
||||
|
@ -748,6 +791,10 @@ impl BaseFlow {
|
|||
pub unsafe fn ref_count<'a>(&'a self) -> &'a AtomicUint {
|
||||
&self.ref_count
|
||||
}
|
||||
|
||||
pub fn debug_id(&self) -> String {
|
||||
format!("{:p}", self as *const _)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ImmutableFlowUtils for &'a Flow {
|
||||
|
|
|
@ -13,6 +13,7 @@ use floats::{ClearBoth, ClearLeft, ClearRight, ClearType};
|
|||
use flow::Flow;
|
||||
use flow;
|
||||
use inline::{InlineFragmentContext, InlineMetrics};
|
||||
use layout_debug;
|
||||
use model::{Auto, IntrinsicISizes, MaybeAuto, Specified, specified};
|
||||
use model;
|
||||
use text;
|
||||
|
@ -33,6 +34,7 @@ use gfx::display_list::{Upright, SidewaysLeft, SidewaysRight};
|
|||
use gfx::font::FontStyle;
|
||||
use gfx::text::glyph::CharIndex;
|
||||
use gfx::text::text_run::TextRun;
|
||||
use serialize::{Encodable, Encoder};
|
||||
use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId};
|
||||
use servo_net::image::holder::ImageHolder;
|
||||
use servo_net::local_image_cache::LocalImageCache;
|
||||
|
@ -104,6 +106,20 @@ pub struct Fragment {
|
|||
/// Holds the style context information for fragments
|
||||
/// that are part of an inline formatting context.
|
||||
pub inline_context: Option<InlineFragmentContext>,
|
||||
|
||||
/// A debug ID that is consistent for the life of
|
||||
/// this fragment (via transform etc).
|
||||
pub debug_id: uint,
|
||||
}
|
||||
|
||||
impl<E, S: Encoder<E>> Encodable<S, E> for Fragment {
|
||||
fn encode(&self, e: &mut S) -> Result<(), E> {
|
||||
e.emit_struct("fragment", 0, |e| {
|
||||
try!(e.emit_struct_field("id", 0, |e| self.debug_id().encode(e)))
|
||||
try!(e.emit_struct_field("border_box", 1, |e| self.border_box.encode(e)))
|
||||
e.emit_struct_field("margin", 2, |e| self.margin.encode(e))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Info specific to the kind of fragment. Keep this enum small.
|
||||
|
@ -336,6 +352,7 @@ impl Fragment {
|
|||
specific: constructor.build_specific_fragment_info_for_node(node),
|
||||
new_line_pos: vec!(),
|
||||
inline_context: None,
|
||||
debug_id: layout_debug::generate_unique_debug_id(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,6 +369,7 @@ impl Fragment {
|
|||
specific: specific,
|
||||
new_line_pos: vec!(),
|
||||
inline_context: None,
|
||||
debug_id: layout_debug::generate_unique_debug_id(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -377,6 +395,7 @@ impl Fragment {
|
|||
specific: specific,
|
||||
new_line_pos: vec!(),
|
||||
inline_context: None,
|
||||
debug_id: layout_debug::generate_unique_debug_id(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -395,13 +414,14 @@ impl Fragment {
|
|||
specific: specific,
|
||||
new_line_pos: vec!(),
|
||||
inline_context: None,
|
||||
debug_id: layout_debug::generate_unique_debug_id(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a debug ID of this fragment. This ID should not be considered stable across multiple
|
||||
/// layouts or fragment manipulations.
|
||||
pub fn debug_id(&self) -> uint {
|
||||
self as *const Fragment as uint
|
||||
self.debug_id
|
||||
}
|
||||
|
||||
/// Transforms this fragment into another fragment of the given type, with the given size, preserving all
|
||||
|
@ -417,6 +437,7 @@ impl Fragment {
|
|||
specific: specific,
|
||||
new_line_pos: self.new_line_pos.clone(),
|
||||
inline_context: self.inline_context.clone(),
|
||||
debug_id: self.debug_id,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@ use floats::{FloatLeft, Floats, PlacementInfo};
|
|||
use flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
|
||||
use flow;
|
||||
use fragment::{Fragment, ScannedTextFragment, ScannedTextFragmentInfo, SplitInfo};
|
||||
use layout_debug;
|
||||
use model::IntrinsicISizes;
|
||||
use text;
|
||||
use wrapper::ThreadSafeLayoutNode;
|
||||
|
@ -58,6 +59,7 @@ use sync::Arc;
|
|||
/// with a float or a horizontal wall of the containing block. The block-start
|
||||
/// inline-start corner of the green zone is the same as that of the line, but
|
||||
/// the green zone can be taller and wider than the line itself.
|
||||
#[deriving(Encodable)]
|
||||
pub struct Line {
|
||||
/// A range of line indices that describe line breaks.
|
||||
///
|
||||
|
@ -140,6 +142,7 @@ pub struct Line {
|
|||
}
|
||||
|
||||
int_range_index! {
|
||||
#[deriving(Encodable)]
|
||||
#[doc = "The index of a fragment in a flattened vector of DOM elements."]
|
||||
struct FragmentIndex(int)
|
||||
}
|
||||
|
@ -147,7 +150,7 @@ int_range_index! {
|
|||
/// A line index consists of two indices: a fragment index that refers to the
|
||||
/// index of a DOM fragment within a flattened inline element; and a glyph index
|
||||
/// where the 0th glyph refers to the first glyph of that fragment.
|
||||
#[deriving(Clone, PartialEq, PartialOrd, Eq, Ord, Zero)]
|
||||
#[deriving(Clone, Encodable, PartialEq, PartialOrd, Eq, Ord, Zero)]
|
||||
pub struct LineIndices {
|
||||
/// The index of a fragment into the flattened vector of DOM elements.
|
||||
///
|
||||
|
@ -625,6 +628,7 @@ impl LineBreaker {
|
|||
}
|
||||
|
||||
/// Represents a list of inline fragments, including element ranges.
|
||||
#[deriving(Encodable)]
|
||||
pub struct InlineFragments {
|
||||
/// The fragments themselves.
|
||||
pub fragments: Vec<Fragment>,
|
||||
|
@ -709,6 +713,7 @@ impl InlineFragments {
|
|||
}
|
||||
|
||||
/// Flows for inline layout.
|
||||
#[deriving(Encodable)]
|
||||
pub struct InlineFlow {
|
||||
/// Data common to all flows.
|
||||
pub base: BaseFlow,
|
||||
|
@ -902,6 +907,8 @@ impl Flow for InlineFlow {
|
|||
}
|
||||
|
||||
fn bubble_inline_sizes(&mut self, _: &LayoutContext) {
|
||||
let _scope = layout_debug::Scope::new(format!("inline::bubble_inline_sizes {:s}", self.base.debug_id()));
|
||||
|
||||
let writing_mode = self.base.writing_mode;
|
||||
for kid in self.base.child_iter() {
|
||||
flow::mut_base(kid).floats = Floats::new(writing_mode);
|
||||
|
@ -927,6 +934,8 @@ impl Flow for InlineFlow {
|
|||
/// Recursively (top-down) determines the actual inline-size of child contexts and fragments. When called
|
||||
/// on this context, the context has had its inline-size set by the parent context.
|
||||
fn assign_inline_sizes(&mut self, _: &LayoutContext) {
|
||||
let _scope = layout_debug::Scope::new(format!("inline::assign_inline_sizes {:s}", self.base.debug_id()));
|
||||
|
||||
// Initialize content fragment inline-sizes if they haven't been initialized already.
|
||||
//
|
||||
// TODO: Combine this with `LineBreaker`'s walk in the fragment list, or put this into `Fragment`.
|
||||
|
@ -955,6 +964,7 @@ impl Flow for InlineFlow {
|
|||
|
||||
/// Calculate and set the block-size of this flow. See CSS 2.1 § 10.6.1.
|
||||
fn assign_block_size(&mut self, ctx: &LayoutContext) {
|
||||
let _scope = layout_debug::Scope::new(format!("inline::assign_block_size {:s}", self.base.debug_id()));
|
||||
debug!("assign_block_size_inline: assigning block_size for flow");
|
||||
|
||||
// Divide the fragments into lines.
|
||||
|
|
|
@ -20,6 +20,7 @@ extern crate gfx;
|
|||
extern crate layout_traits;
|
||||
extern crate script;
|
||||
extern crate script_traits;
|
||||
extern crate serialize;
|
||||
extern crate style;
|
||||
#[phase(plugin)]
|
||||
extern crate servo_macros = "macros";
|
||||
|
@ -42,6 +43,7 @@ pub mod flow;
|
|||
pub mod flow_list;
|
||||
pub mod flow_ref;
|
||||
pub mod fragment;
|
||||
pub mod layout_debug;
|
||||
pub mod layout_task;
|
||||
pub mod inline;
|
||||
pub mod model;
|
||||
|
|
112
src/components/layout/layout_debug.rs
Normal file
112
src/components/layout/layout_debug.rs
Normal file
|
@ -0,0 +1,112 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
//! Supports writing a trace file created during each layout scope
|
||||
//! that can be viewed by an external tool to make layout debugging easier.
|
||||
|
||||
use flow_ref::FlowRef;
|
||||
use serialize::json;
|
||||
use std::cell::RefCell;
|
||||
use std::io::File;
|
||||
use std::sync::atomics::{AtomicUint, SeqCst, INIT_ATOMIC_UINT};
|
||||
|
||||
local_data_key!(state_key: RefCell<State>)
|
||||
|
||||
static mut DEBUG_ID_COUNTER: AtomicUint = INIT_ATOMIC_UINT;
|
||||
|
||||
pub struct Scope;
|
||||
|
||||
#[deriving(Encodable)]
|
||||
struct ScopeData {
|
||||
name: String,
|
||||
pre: String,
|
||||
post: String,
|
||||
children: Vec<Box<ScopeData>>,
|
||||
}
|
||||
|
||||
impl ScopeData {
|
||||
fn new(name: String, pre: String) -> ScopeData {
|
||||
ScopeData {
|
||||
name: name,
|
||||
pre: pre,
|
||||
post: String::new(),
|
||||
children: vec!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
flow_root: FlowRef,
|
||||
scope_stack: Vec<Box<ScopeData>>,
|
||||
}
|
||||
|
||||
/// A layout debugging scope. The entire state of the flow tree
|
||||
/// will be output at the beginning and end of this scope.
|
||||
impl Scope {
|
||||
pub fn new(name: String) -> Scope {
|
||||
let maybe_refcell = state_key.get();
|
||||
match maybe_refcell {
|
||||
Some(refcell) => {
|
||||
let mut state = refcell.borrow_mut();
|
||||
let flow_trace = json::encode(&state.flow_root.get());
|
||||
let data = box ScopeData::new(name, flow_trace);
|
||||
state.scope_stack.push(data);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
Scope
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Scope {
|
||||
fn drop(&mut self) {
|
||||
let maybe_refcell = state_key.get();
|
||||
match maybe_refcell {
|
||||
Some(refcell) => {
|
||||
let mut state = refcell.borrow_mut();
|
||||
let mut current_scope = state.scope_stack.pop().unwrap();
|
||||
current_scope.post = json::encode(&state.flow_root.get());
|
||||
let previous_scope = state.scope_stack.mut_last().unwrap();
|
||||
previous_scope.children.push(current_scope);
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a unique ID. This is used for items such as Fragment
|
||||
/// which are often reallocated but represent essentially the
|
||||
/// same data.
|
||||
pub fn generate_unique_debug_id() -> uint {
|
||||
unsafe { DEBUG_ID_COUNTER.fetch_add(1, SeqCst) }
|
||||
}
|
||||
|
||||
/// Begin a layout debug trace. If this has not been called,
|
||||
/// creating debug scopes has no effect.
|
||||
pub fn begin_trace(flow_root: FlowRef) {
|
||||
assert!(state_key.get().is_none());
|
||||
|
||||
let flow_trace = json::encode(&flow_root.get());
|
||||
let state = State {
|
||||
scope_stack: vec![box ScopeData::new("root".to_string(), flow_trace)],
|
||||
flow_root: flow_root,
|
||||
};
|
||||
state_key.replace(Some(RefCell::new(state)));
|
||||
}
|
||||
|
||||
/// End the debug layout trace. This will write the layout
|
||||
/// trace to disk in the current directory. The output
|
||||
/// file can then be viewed with an external tool.
|
||||
pub fn end_trace() {
|
||||
let task_state_cell = state_key.replace(None).unwrap();
|
||||
let mut task_state = task_state_cell.borrow_mut();
|
||||
assert!(task_state.scope_stack.len() == 1);
|
||||
let mut root_scope = task_state.scope_stack.pop().unwrap();
|
||||
root_scope.post = json::encode(&task_state.flow_root.get());
|
||||
|
||||
let result = json::encode(&root_scope);
|
||||
let path = Path::new("layout_trace.json");
|
||||
let mut file = File::create(&path).unwrap();
|
||||
file.write_str(result.as_slice()).unwrap();
|
||||
}
|
|
@ -14,6 +14,7 @@ use flow::{PreorderFlowTraversal, PostorderFlowTraversal};
|
|||
use flow;
|
||||
use flow_ref::FlowRef;
|
||||
use incremental::RestyleDamage;
|
||||
use layout_debug;
|
||||
use parallel::UnsafeFlow;
|
||||
use parallel;
|
||||
use util::{LayoutDataAccess, LayoutDataWrapper, OpaqueNodeMethods, ToGfxColor};
|
||||
|
@ -530,6 +531,8 @@ impl LayoutTask {
|
|||
fn solve_constraints<'a>(&mut self,
|
||||
layout_root: &mut Flow,
|
||||
layout_context: &'a LayoutContext<'a>) {
|
||||
let _scope = layout_debug::Scope::new("solve_constraints".to_string());
|
||||
|
||||
if layout_context.shared.opts.bubble_inline_sizes_separately {
|
||||
let mut traversal = BubbleISizesTraversal {
|
||||
layout_context: layout_context,
|
||||
|
@ -667,6 +670,10 @@ impl LayoutTask {
|
|||
// memory safety but is a useful debugging tool.)
|
||||
self.verify_flow_tree(&mut layout_root);
|
||||
|
||||
if self.opts.trace_layout {
|
||||
layout_debug::begin_trace(layout_root.clone());
|
||||
}
|
||||
|
||||
// Propagate damage.
|
||||
profile(time::LayoutDamagePropagateCategory, self.time_profiler_chan.clone(), || {
|
||||
layout_root.get_mut().traverse_preorder(&mut PropagateDamageTraversal {
|
||||
|
@ -778,6 +785,10 @@ impl LayoutTask {
|
|||
});
|
||||
}
|
||||
|
||||
if self.opts.trace_layout {
|
||||
layout_debug::end_trace();
|
||||
}
|
||||
|
||||
// Tell script that we're done.
|
||||
//
|
||||
// FIXME(pcwalton): This should probably be *one* channel, but we can't fix this without
|
||||
|
|
|
@ -243,6 +243,7 @@ pub enum MarginCollapseState {
|
|||
}
|
||||
|
||||
/// Intrinsic inline-sizes, which consist of minimum and preferred.
|
||||
#[deriving(Encodable)]
|
||||
pub struct IntrinsicISizes {
|
||||
/// The *minimum inline-size* of the content.
|
||||
pub minimum_inline_size: Au,
|
||||
|
|
|
@ -7,6 +7,7 @@ use geom::point::Point2D;
|
|||
use geom::rect::Rect;
|
||||
use geom::size::Size2D;
|
||||
|
||||
use serialize::{Encodable, Encoder};
|
||||
use std::default::Default;
|
||||
use std::num::{NumCast, One, Zero};
|
||||
use std::fmt;
|
||||
|
@ -71,6 +72,12 @@ impl Default for Au {
|
|||
}
|
||||
}
|
||||
|
||||
impl<E, S: Encoder<E>> Encodable<S, E> for Au {
|
||||
fn encode(&self, e: &mut S) -> Result<(), E> {
|
||||
e.emit_f64(to_frac_px(*self))
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Show for Au {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}px", to_frac_px(*self))
|
||||
|
|
|
@ -9,8 +9,8 @@ use std::cmp::{min, max};
|
|||
use std::fmt::{Show, Formatter, FormatError};
|
||||
use std::num::Zero;
|
||||
|
||||
|
||||
bitflags!(
|
||||
#[deriving(Encodable)]
|
||||
flags WritingMode: u8 {
|
||||
static FlagRTL = 1 << 0,
|
||||
static FlagVertical = 1 << 1,
|
||||
|
@ -79,11 +79,11 @@ impl Show for WritingMode {
|
|||
/// (in addition to taking it as a parameter to methods) and check it.
|
||||
/// In non-debug builds, make this storage zero-size and the checks no-ops.
|
||||
#[cfg(ndebug)]
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
#[deriving(Encodable, PartialEq, Eq, Clone)]
|
||||
struct DebugWritingMode;
|
||||
|
||||
#[cfg(not(ndebug))]
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
#[deriving(Encodable, PartialEq, Eq, Clone)]
|
||||
struct DebugWritingMode {
|
||||
mode: WritingMode
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ impl Show for DebugWritingMode {
|
|||
|
||||
|
||||
/// A 2D size in flow-relative dimensions
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
#[deriving(Encodable, PartialEq, Eq, Clone)]
|
||||
pub struct LogicalSize<T> {
|
||||
pub inline: T, // inline-size, a.k.a. logical width, a.k.a. measure
|
||||
pub block: T, // block-size, a.k.a. logical height, a.k.a. extent
|
||||
|
@ -271,7 +271,7 @@ impl<T: Sub<T, T>> Sub<LogicalSize<T>, LogicalSize<T>> for LogicalSize<T> {
|
|||
|
||||
|
||||
/// A 2D point in flow-relative dimensions
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
#[deriving(PartialEq, Encodable, Eq, Clone)]
|
||||
pub struct LogicalPoint<T> {
|
||||
pub i: T, /// inline-axis coordinate
|
||||
pub b: T, /// block-axis coordinate
|
||||
|
@ -444,7 +444,7 @@ impl<T: Sub<T,T>> Sub<LogicalSize<T>, LogicalPoint<T>> for LogicalPoint<T> {
|
|||
/// Represents the four sides of the margins, borders, or padding of a CSS box,
|
||||
/// or a combination of those.
|
||||
/// A positive "margin" can be added to a rectangle to obtain a bigger rectangle.
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
#[deriving(Encodable, PartialEq, Eq, Clone)]
|
||||
pub struct LogicalMargin<T> {
|
||||
pub block_start: T,
|
||||
pub inline_end: T,
|
||||
|
@ -726,7 +726,7 @@ impl<T: Sub<T, T>> Sub<LogicalMargin<T>, LogicalMargin<T>> for LogicalMargin<T>
|
|||
|
||||
|
||||
/// A rectangle in flow-relative dimensions
|
||||
#[deriving(PartialEq, Eq, Clone)]
|
||||
#[deriving(Encodable, PartialEq, Eq, Clone)]
|
||||
pub struct LogicalRect<T> {
|
||||
pub start: LogicalPoint<T>,
|
||||
pub size: LogicalSize<T>,
|
||||
|
|
|
@ -78,6 +78,11 @@ pub struct Opts {
|
|||
/// where pixel perfect results are required when using fonts such as the Ahem
|
||||
/// font for layout tests.
|
||||
pub enable_text_antialiasing: bool,
|
||||
|
||||
/// True if each step of layout is traced to an external JSON file
|
||||
/// for debugging purposes. Settings this implies sequential layout
|
||||
/// and render.
|
||||
pub trace_layout: bool,
|
||||
}
|
||||
|
||||
fn print_usage(app: &str, opts: &[getopts::OptGroup]) {
|
||||
|
@ -111,6 +116,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
|
|||
getopts::optflag("b", "bubble-widths", "Bubble intrinsic widths separately like other engines"),
|
||||
getopts::optflag("", "show-debug-borders", "Show debugging borders on layers and tiles."),
|
||||
getopts::optflag("", "disable-text-aa", "Disable antialiasing for text rendering."),
|
||||
getopts::optflag("", "trace-layout", "Write layout trace to external file for debugging."),
|
||||
getopts::optflag("h", "help", "Print this message")
|
||||
);
|
||||
|
||||
|
@ -163,7 +169,7 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
|
|||
ScaleFactor(from_str(dppx_str.as_slice()).unwrap())
|
||||
);
|
||||
|
||||
let n_render_threads: uint = match opt_match.opt_str("t") {
|
||||
let mut n_render_threads: uint = match opt_match.opt_str("t") {
|
||||
Some(n_render_threads_str) => from_str(n_render_threads_str.as_slice()).unwrap(),
|
||||
None => 1, // FIXME: Number of cores.
|
||||
};
|
||||
|
@ -178,11 +184,20 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
|
|||
|
||||
let cpu_painting = opt_match.opt_present("c");
|
||||
|
||||
let layout_threads: uint = match opt_match.opt_str("y") {
|
||||
let mut layout_threads: uint = match opt_match.opt_str("y") {
|
||||
Some(layout_threads_str) => from_str(layout_threads_str.as_slice()).unwrap(),
|
||||
None => cmp::max(rt::default_sched_threads() * 3 / 4, 1),
|
||||
};
|
||||
|
||||
let mut bubble_inline_sizes_separately = opt_match.opt_present("b");
|
||||
|
||||
let trace_layout = opt_match.opt_present("trace-layout");
|
||||
if trace_layout {
|
||||
n_render_threads = 1;
|
||||
layout_threads = 1;
|
||||
bubble_inline_sizes_separately = true;
|
||||
}
|
||||
|
||||
Some(Opts {
|
||||
urls: urls,
|
||||
render_backend: render_backend,
|
||||
|
@ -198,9 +213,10 @@ pub fn from_cmdline_args(args: &[String]) -> Option<Opts> {
|
|||
output_file: opt_match.opt_str("o"),
|
||||
headless: opt_match.opt_present("z"),
|
||||
hard_fail: opt_match.opt_present("f"),
|
||||
bubble_inline_sizes_separately: opt_match.opt_present("b"),
|
||||
bubble_inline_sizes_separately: bubble_inline_sizes_separately,
|
||||
show_debug_borders: opt_match.opt_present("show-debug-borders"),
|
||||
enable_text_antialiasing: !opt_match.opt_present("disable-text-aa"),
|
||||
trace_layout: trace_layout,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -100,7 +100,7 @@ pub enum RangeRelation<I> {
|
|||
}
|
||||
|
||||
/// A range of indices
|
||||
#[deriving(Clone)]
|
||||
#[deriving(Clone, Encodable)]
|
||||
pub struct Range<I> {
|
||||
begin: I,
|
||||
length: I,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue