mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Dump box tree state into json files and display it on layout 2020 viewer
This commit is contained in:
parent
aaa3cd9a59
commit
a042f85083
11 changed files with 161 additions and 56 deletions
|
@ -10,7 +10,7 @@ use crate::style_ext::{ComputedValuesExt, DisplayInside};
|
|||
use servo_arc::Arc;
|
||||
use style::properties::ComputedValues;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct FloatBox {
|
||||
pub contents: IndependentFormattingContext,
|
||||
}
|
||||
|
|
|
@ -23,12 +23,12 @@ use style::values::specified::text::TextAlignKeyword;
|
|||
use style::Zero;
|
||||
use webrender_api::FontInstanceKey;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, Serialize)]
|
||||
pub(crate) struct InlineFormattingContext {
|
||||
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) enum InlineLevelBox {
|
||||
InlineBox(InlineBox),
|
||||
TextRun(TextRun),
|
||||
|
@ -37,9 +37,10 @@ pub(crate) enum InlineLevelBox {
|
|||
Atomic(IndependentFormattingContext),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct InlineBox {
|
||||
pub tag: OpaqueNode,
|
||||
#[serde(skip_serializing)]
|
||||
pub style: Arc<ComputedValues>,
|
||||
pub first_fragment: bool,
|
||||
pub last_fragment: bool,
|
||||
|
@ -47,9 +48,10 @@ pub(crate) struct InlineBox {
|
|||
}
|
||||
|
||||
/// https://www.w3.org/TR/css-display-3/#css-text-run
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct TextRun {
|
||||
pub tag: OpaqueNode,
|
||||
#[serde(skip_serializing)]
|
||||
pub parent_style: Arc<ComputedValues>,
|
||||
pub text: String,
|
||||
}
|
||||
|
|
|
@ -31,22 +31,23 @@ mod root;
|
|||
|
||||
pub use root::{BoxTreeRoot, FragmentTreeRoot};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct BlockFormattingContext {
|
||||
pub contents: BlockContainer,
|
||||
pub contains_floats: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) enum BlockContainer {
|
||||
BlockLevelBoxes(Vec<Arc<BlockLevelBox>>),
|
||||
InlineFormattingContext(InlineFormattingContext),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) enum BlockLevelBox {
|
||||
SameFormattingContextBlock {
|
||||
tag: OpaqueNode,
|
||||
#[serde(skip_serializing)]
|
||||
style: Arc<ComputedValues>,
|
||||
contents: BlockContainer,
|
||||
},
|
||||
|
|
|
@ -28,6 +28,7 @@ use style::properties::ComputedValues;
|
|||
use style::values::computed::Length;
|
||||
use style_traits::CSSPixel;
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct BoxTreeRoot(BlockFormattingContext);
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
|
|
@ -18,9 +18,10 @@ use style::properties::ComputedValues;
|
|||
use style::values::computed::Length;
|
||||
|
||||
/// https://drafts.csswg.org/css-display/#independent-formatting-context
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct IndependentFormattingContext {
|
||||
pub tag: OpaqueNode,
|
||||
#[serde(skip_serializing)]
|
||||
pub style: Arc<ComputedValues>,
|
||||
|
||||
/// If it was requested during construction
|
||||
|
@ -38,7 +39,7 @@ pub(crate) struct IndependentLayout {
|
|||
|
||||
// Private so that code outside of this module cannot match variants.
|
||||
// It should got through methods instead.
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
enum IndependentFormattingContextContents {
|
||||
Flow(BlockFormattingContext),
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
//! Supports writing a trace file created during each layout scope
|
||||
//! that can be viewed by an external tool to make layout debugging easier.
|
||||
|
||||
use crate::flow::FragmentTreeRoot;
|
||||
use crate::flow::{BoxTreeRoot, FragmentTreeRoot};
|
||||
use serde_json::{to_string, to_value, Value};
|
||||
use std::cell::RefCell;
|
||||
use std::fs::File;
|
||||
|
@ -32,38 +32,52 @@ macro_rules! layout_debug_scope(
|
|||
)
|
||||
);
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct TreeValues {
|
||||
pub box_tree: Value,
|
||||
pub fragment_tree: Value,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ScopeData {
|
||||
name: String,
|
||||
pre: Value,
|
||||
post: Value,
|
||||
pre: TreeValues,
|
||||
post: TreeValues,
|
||||
children: Vec<Box<ScopeData>>,
|
||||
}
|
||||
|
||||
impl ScopeData {
|
||||
fn new(name: String, pre: Value) -> ScopeData {
|
||||
fn new(name: String, box_tree: Value, fragment_tree: Value) -> ScopeData {
|
||||
ScopeData {
|
||||
name: name,
|
||||
pre: pre,
|
||||
post: Value::Null,
|
||||
name,
|
||||
pre: TreeValues {
|
||||
box_tree,
|
||||
fragment_tree,
|
||||
},
|
||||
post: TreeValues {
|
||||
box_tree: Value::Null,
|
||||
fragment_tree: Value::Null,
|
||||
},
|
||||
children: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct State {
|
||||
fragment: Arc<FragmentTreeRoot>,
|
||||
fragment_tree: Arc<FragmentTreeRoot>,
|
||||
box_tree: Arc<BoxTreeRoot>,
|
||||
scope_stack: Vec<Box<ScopeData>>,
|
||||
}
|
||||
|
||||
/// A layout debugging scope. The entire state of the fragment tree
|
||||
/// A layout debugging scope. The entire state of the box and fragment trees
|
||||
/// will be output at the beginning and end of this scope.
|
||||
impl Scope {
|
||||
pub fn new(name: String) -> Scope {
|
||||
STATE_KEY.with(|ref r| {
|
||||
if let Some(ref mut state) = *r.borrow_mut() {
|
||||
let fragment_tree = to_value(&state.fragment).unwrap();
|
||||
let data = Box::new(ScopeData::new(name.clone(), fragment_tree));
|
||||
let box_tree = to_value(&state.box_tree).unwrap();
|
||||
let fragment_tree = to_value(&state.fragment_tree).unwrap();
|
||||
let data = Box::new(ScopeData::new(name.clone(), box_tree, fragment_tree));
|
||||
state.scope_stack.push(data);
|
||||
}
|
||||
});
|
||||
|
@ -77,7 +91,10 @@ impl Drop for Scope {
|
|||
STATE_KEY.with(|ref r| {
|
||||
if let Some(ref mut state) = *r.borrow_mut() {
|
||||
let mut current_scope = state.scope_stack.pop().unwrap();
|
||||
current_scope.post = to_value(&state.fragment).unwrap();
|
||||
current_scope.post = TreeValues {
|
||||
box_tree: to_value(&state.box_tree).unwrap(),
|
||||
fragment_tree: to_value(&state.fragment_tree).unwrap(),
|
||||
};
|
||||
let previous_scope = state.scope_stack.last_mut().unwrap();
|
||||
previous_scope.children.push(current_scope);
|
||||
}
|
||||
|
@ -93,14 +110,20 @@ pub fn generate_unique_debug_id() -> u16 {
|
|||
|
||||
/// Begin a layout debug trace. If this has not been called,
|
||||
/// creating debug scopes has no effect.
|
||||
pub fn begin_trace(root: Arc<FragmentTreeRoot>) {
|
||||
pub fn begin_trace(box_tree: Arc<BoxTreeRoot>, fragment_tree: Arc<FragmentTreeRoot>) {
|
||||
assert!(STATE_KEY.with(|ref r| r.borrow().is_none()));
|
||||
|
||||
STATE_KEY.with(|ref r| {
|
||||
let root_trace = to_value(&root).unwrap();
|
||||
let box_tree_value = to_value(&box_tree).unwrap();
|
||||
let fragment_tree_value = to_value(&fragment_tree).unwrap();
|
||||
let state = State {
|
||||
scope_stack: vec![Box::new(ScopeData::new("root".to_owned(), root_trace))],
|
||||
fragment: root.clone(),
|
||||
scope_stack: vec![Box::new(ScopeData::new(
|
||||
"root".to_owned(),
|
||||
box_tree_value,
|
||||
fragment_tree_value,
|
||||
))],
|
||||
box_tree,
|
||||
fragment_tree,
|
||||
};
|
||||
*r.borrow_mut() = Some(state);
|
||||
});
|
||||
|
@ -113,8 +136,10 @@ pub fn end_trace(generation: u32) {
|
|||
let mut thread_state = STATE_KEY.with(|ref r| r.borrow_mut().take().unwrap());
|
||||
assert_eq!(thread_state.scope_stack.len(), 1);
|
||||
let mut root_scope = thread_state.scope_stack.pop().unwrap();
|
||||
root_scope.post = to_value(&thread_state.fragment).unwrap();
|
||||
|
||||
root_scope.post = TreeValues {
|
||||
box_tree: to_value(&thread_state.box_tree).unwrap_or(Value::Null),
|
||||
fragment_tree: to_value(&thread_state.fragment_tree).unwrap_or(Value::Null),
|
||||
};
|
||||
let result = to_string(&root_scope).unwrap();
|
||||
let mut file = File::create(format!("layout_trace-{}.json", generation)).unwrap();
|
||||
file.write_all(result.as_bytes()).unwrap();
|
||||
|
|
|
@ -18,7 +18,7 @@ use style::properties::ComputedValues;
|
|||
use style::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
||||
use style::Zero;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct AbsolutelyPositionedBox {
|
||||
pub contents: IndependentFormattingContext,
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ use style::values::computed::{Length, LengthOrAuto};
|
|||
use style::values::CSSFloat;
|
||||
use style::Zero;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) struct ReplacedContent {
|
||||
pub kind: ReplacedContentKind,
|
||||
intrinsic: IntrinsicSizes,
|
||||
|
@ -41,7 +41,7 @@ pub(crate) struct IntrinsicSizes {
|
|||
pub ratio: Option<CSSFloat>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) enum ReplacedContentKind {
|
||||
Image(Option<Arc<Image>>),
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ impl ContentSizesRequest {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, Serialize)]
|
||||
pub(crate) struct ContentSizes {
|
||||
pub min_content: Length,
|
||||
pub max_content: Length,
|
||||
|
@ -83,7 +83,7 @@ impl ContentSizes {
|
|||
}
|
||||
|
||||
/// Optional min/max-content for storage in the box tree
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub(crate) enum BoxContentSizes {
|
||||
NoneWereRequested, // … during box construction
|
||||
Inline(ContentSizes),
|
||||
|
|
|
@ -173,7 +173,7 @@ pub struct LayoutThread {
|
|||
outstanding_web_fonts: Arc<AtomicUsize>,
|
||||
|
||||
/// The root of the box tree.
|
||||
box_tree_root: RefCell<Option<BoxTreeRoot>>,
|
||||
box_tree_root: RefCell<Option<Arc<BoxTreeRoot>>>,
|
||||
|
||||
/// The root of the fragment tree.
|
||||
fragment_tree_root: RefCell<Option<Arc<FragmentTreeRoot>>>,
|
||||
|
@ -1154,7 +1154,8 @@ impl LayoutThread {
|
|||
} else {
|
||||
build_box_tree()
|
||||
};
|
||||
Some(box_tree)
|
||||
|
||||
Some(Arc::new(box_tree))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -1167,13 +1168,13 @@ impl LayoutThread {
|
|||
self.viewport_size.height.to_f32_px(),
|
||||
);
|
||||
let run_layout = || box_tree.layout(&layout_context, viewport_size);
|
||||
let fragment_tree = if let Some(pool) = rayon_pool {
|
||||
let fragment_tree = Arc::new(if let Some(pool) = rayon_pool {
|
||||
pool.install(run_layout)
|
||||
} else {
|
||||
run_layout()
|
||||
};
|
||||
});
|
||||
*self.box_tree_root.borrow_mut() = Some(box_tree);
|
||||
*self.fragment_tree_root.borrow_mut() = Some(Arc::new(fragment_tree));
|
||||
*self.fragment_tree_root.borrow_mut() = Some(fragment_tree);
|
||||
}
|
||||
|
||||
for element in elements_with_snapshot {
|
||||
|
@ -1382,6 +1383,12 @@ impl LayoutThread {
|
|||
document: Option<&ServoLayoutDocument>,
|
||||
context: &mut LayoutContext,
|
||||
) {
|
||||
if self.trace_layout {
|
||||
if let Some(box_tree) = &*self.box_tree_root.borrow() {
|
||||
layout_debug::begin_trace(box_tree.clone(), fragment_tree.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if !reflow_goal.needs_display() {
|
||||
// Defer the paint step until the next ForDisplay.
|
||||
//
|
||||
|
@ -1393,10 +1400,6 @@ impl LayoutThread {
|
|||
return;
|
||||
}
|
||||
|
||||
if self.trace_layout {
|
||||
layout_debug::begin_trace(fragment_tree.clone());
|
||||
}
|
||||
|
||||
if let Some(document) = document {
|
||||
document.will_paint();
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue