From acedb166707aa2e8fd02e6c7d943147394c34609 Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 3 Sep 2014 11:52:40 +1000 Subject: [PATCH] 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. --- src/components/embedding/core.rs | 1 + src/components/gfx/gfx.rs | 1 + src/components/gfx/text/glyph.rs | 1 + src/components/layout/block.rs | 15 ++++ src/components/layout/floats.rs | 2 +- src/components/layout/flow.rs | 51 ++++++++++- src/components/layout/fragment.rs | 23 ++++- src/components/layout/inline.rs | 12 ++- src/components/layout/layout.rs | 2 + src/components/layout/layout_debug.rs | 112 ++++++++++++++++++++++++ src/components/layout/layout_task.rs | 11 +++ src/components/layout/model.rs | 1 + src/components/util/geometry.rs | 7 ++ src/components/util/logical_geometry.rs | 14 +-- src/components/util/opts.rs | 22 ++++- src/components/util/range.rs | 2 +- 16 files changed, 261 insertions(+), 16 deletions(-) create mode 100644 src/components/layout/layout_debug.rs diff --git a/src/components/embedding/core.rs b/src/components/embedding/core.rs index df92878962d..9ff237f2d82 100644 --- a/src/components/embedding/core.rs +++ b/src/components/embedding/core.rs @@ -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); diff --git a/src/components/gfx/gfx.rs b/src/components/gfx/gfx.rs index bc326a51548..2cd53af5a67 100644 --- a/src/components/gfx/gfx.rs +++ b/src/components/gfx/gfx.rs @@ -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"; diff --git a/src/components/gfx/text/glyph.rs b/src/components/gfx/text/glyph.rs index fa1091f7766..2ea2d7c5d2e 100644 --- a/src/components/gfx/text/glyph.rs +++ b/src/components/gfx/text/glyph.rs @@ -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) diff --git a/src/components/layout/block.rs b/src/components/layout/block.rs index 5b433e44636..269dbccdb76 100644 --- a/src/components/layout/block.rs +++ b/src/components/layout/block.rs @@ -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" diff --git a/src/components/layout/floats.rs b/src/components/layout/floats.rs index 94ad8c940f6..94017e6b3f7 100644 --- a/src/components/layout/floats.rs +++ b/src/components/layout/floats.rs @@ -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 diff --git a/src/components/layout/flow.rs b/src/components/layout/flow.rs index 74346633d36..2759ebbe74b 100644 --- a/src/components/layout/flow.rs +++ b/src/components/layout/flow.rs @@ -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> Encodable 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, 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, @@ -697,6 +720,26 @@ pub struct BaseFlow { pub writing_mode: WritingMode, } +impl> Encodable 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 { diff --git a/src/components/layout/fragment.rs b/src/components/layout/fragment.rs index 876ca785107..cf77c5061b7 100644 --- a/src/components/layout/fragment.rs +++ b/src/components/layout/fragment.rs @@ -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, + + /// A debug ID that is consistent for the life of + /// this fragment (via transform etc). + pub debug_id: uint, +} + +impl> Encodable 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, } } diff --git a/src/components/layout/inline.rs b/src/components/layout/inline.rs index c5157709c27..6fc05e00cab 100644 --- a/src/components/layout/inline.rs +++ b/src/components/layout/inline.rs @@ -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, @@ -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. diff --git a/src/components/layout/layout.rs b/src/components/layout/layout.rs index 0157ceb8e41..51f82654323 100644 --- a/src/components/layout/layout.rs +++ b/src/components/layout/layout.rs @@ -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; diff --git a/src/components/layout/layout_debug.rs b/src/components/layout/layout_debug.rs new file mode 100644 index 00000000000..bf4cbbfca2a --- /dev/null +++ b/src/components/layout/layout_debug.rs @@ -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) + +static mut DEBUG_ID_COUNTER: AtomicUint = INIT_ATOMIC_UINT; + +pub struct Scope; + +#[deriving(Encodable)] +struct ScopeData { + name: String, + pre: String, + post: String, + children: Vec>, +} + +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>, +} + +/// 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(); +} diff --git a/src/components/layout/layout_task.rs b/src/components/layout/layout_task.rs index e104ea443fe..93b49652678 100644 --- a/src/components/layout/layout_task.rs +++ b/src/components/layout/layout_task.rs @@ -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 diff --git a/src/components/layout/model.rs b/src/components/layout/model.rs index 65f0c3704e3..23647b1b77d 100644 --- a/src/components/layout/model.rs +++ b/src/components/layout/model.rs @@ -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, diff --git a/src/components/util/geometry.rs b/src/components/util/geometry.rs index 30f660e2256..c87e98e38b7 100644 --- a/src/components/util/geometry.rs +++ b/src/components/util/geometry.rs @@ -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> Encodable 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)) diff --git a/src/components/util/logical_geometry.rs b/src/components/util/logical_geometry.rs index 8f2356a5f88..a16dd6a5c8d 100644 --- a/src/components/util/logical_geometry.rs +++ b/src/components/util/logical_geometry.rs @@ -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 { 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> Sub, LogicalSize> for LogicalSize { /// A 2D point in flow-relative dimensions -#[deriving(PartialEq, Eq, Clone)] +#[deriving(PartialEq, Encodable, Eq, Clone)] pub struct LogicalPoint { pub i: T, /// inline-axis coordinate pub b: T, /// block-axis coordinate @@ -444,7 +444,7 @@ impl> Sub, LogicalPoint> for LogicalPoint { /// 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 { pub block_start: T, pub inline_end: T, @@ -726,7 +726,7 @@ impl> Sub, LogicalMargin> for LogicalMargin /// A rectangle in flow-relative dimensions -#[deriving(PartialEq, Eq, Clone)] +#[deriving(Encodable, PartialEq, Eq, Clone)] pub struct LogicalRect { pub start: LogicalPoint, pub size: LogicalSize, diff --git a/src/components/util/opts.rs b/src/components/util/opts.rs index 8131e98a994..40da68632d8 100644 --- a/src/components/util/opts.rs +++ b/src/components/util/opts.rs @@ -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 { 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 { 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 { 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 { 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, }) } diff --git a/src/components/util/range.rs b/src/components/util/range.rs index 23dad9c4f69..ffea08e6264 100644 --- a/src/components/util/range.rs +++ b/src/components/util/range.rs @@ -100,7 +100,7 @@ pub enum RangeRelation { } /// A range of indices -#[deriving(Clone)] +#[deriving(Clone, Encodable)] pub struct Range { begin: I, length: I,