mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +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 servo_arc::Arc;
|
||||||
use style::properties::ComputedValues;
|
use style::properties::ComputedValues;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct FloatBox {
|
pub(crate) struct FloatBox {
|
||||||
pub contents: IndependentFormattingContext,
|
pub contents: IndependentFormattingContext,
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,12 +23,12 @@ use style::values::specified::text::TextAlignKeyword;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
use webrender_api::FontInstanceKey;
|
use webrender_api::FontInstanceKey;
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Serialize)]
|
||||||
pub(crate) struct InlineFormattingContext {
|
pub(crate) struct InlineFormattingContext {
|
||||||
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
|
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) enum InlineLevelBox {
|
pub(crate) enum InlineLevelBox {
|
||||||
InlineBox(InlineBox),
|
InlineBox(InlineBox),
|
||||||
TextRun(TextRun),
|
TextRun(TextRun),
|
||||||
|
@ -37,9 +37,10 @@ pub(crate) enum InlineLevelBox {
|
||||||
Atomic(IndependentFormattingContext),
|
Atomic(IndependentFormattingContext),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct InlineBox {
|
pub(crate) struct InlineBox {
|
||||||
pub tag: OpaqueNode,
|
pub tag: OpaqueNode,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub style: Arc<ComputedValues>,
|
pub style: Arc<ComputedValues>,
|
||||||
pub first_fragment: bool,
|
pub first_fragment: bool,
|
||||||
pub last_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
|
/// https://www.w3.org/TR/css-display-3/#css-text-run
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct TextRun {
|
pub(crate) struct TextRun {
|
||||||
pub tag: OpaqueNode,
|
pub tag: OpaqueNode,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub parent_style: Arc<ComputedValues>,
|
pub parent_style: Arc<ComputedValues>,
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,22 +31,23 @@ mod root;
|
||||||
|
|
||||||
pub use root::{BoxTreeRoot, FragmentTreeRoot};
|
pub use root::{BoxTreeRoot, FragmentTreeRoot};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct BlockFormattingContext {
|
pub(crate) struct BlockFormattingContext {
|
||||||
pub contents: BlockContainer,
|
pub contents: BlockContainer,
|
||||||
pub contains_floats: bool,
|
pub contains_floats: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) enum BlockContainer {
|
pub(crate) enum BlockContainer {
|
||||||
BlockLevelBoxes(Vec<Arc<BlockLevelBox>>),
|
BlockLevelBoxes(Vec<Arc<BlockLevelBox>>),
|
||||||
InlineFormattingContext(InlineFormattingContext),
|
InlineFormattingContext(InlineFormattingContext),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) enum BlockLevelBox {
|
pub(crate) enum BlockLevelBox {
|
||||||
SameFormattingContextBlock {
|
SameFormattingContextBlock {
|
||||||
tag: OpaqueNode,
|
tag: OpaqueNode,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
style: Arc<ComputedValues>,
|
style: Arc<ComputedValues>,
|
||||||
contents: BlockContainer,
|
contents: BlockContainer,
|
||||||
},
|
},
|
||||||
|
|
|
@ -28,6 +28,7 @@ use style::properties::ComputedValues;
|
||||||
use style::values::computed::Length;
|
use style::values::computed::Length;
|
||||||
use style_traits::CSSPixel;
|
use style_traits::CSSPixel;
|
||||||
|
|
||||||
|
#[derive(Serialize)]
|
||||||
pub struct BoxTreeRoot(BlockFormattingContext);
|
pub struct BoxTreeRoot(BlockFormattingContext);
|
||||||
|
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
|
|
@ -18,9 +18,10 @@ use style::properties::ComputedValues;
|
||||||
use style::values::computed::Length;
|
use style::values::computed::Length;
|
||||||
|
|
||||||
/// https://drafts.csswg.org/css-display/#independent-formatting-context
|
/// https://drafts.csswg.org/css-display/#independent-formatting-context
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct IndependentFormattingContext {
|
pub(crate) struct IndependentFormattingContext {
|
||||||
pub tag: OpaqueNode,
|
pub tag: OpaqueNode,
|
||||||
|
#[serde(skip_serializing)]
|
||||||
pub style: Arc<ComputedValues>,
|
pub style: Arc<ComputedValues>,
|
||||||
|
|
||||||
/// If it was requested during construction
|
/// 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.
|
// Private so that code outside of this module cannot match variants.
|
||||||
// It should got through methods instead.
|
// It should got through methods instead.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
enum IndependentFormattingContextContents {
|
enum IndependentFormattingContextContents {
|
||||||
Flow(BlockFormattingContext),
|
Flow(BlockFormattingContext),
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
//! Supports writing a trace file created during each layout scope
|
//! Supports writing a trace file created during each layout scope
|
||||||
//! that can be viewed by an external tool to make layout debugging easier.
|
//! 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 serde_json::{to_string, to_value, Value};
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fs::File;
|
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)]
|
#[derive(Serialize)]
|
||||||
struct ScopeData {
|
struct ScopeData {
|
||||||
name: String,
|
name: String,
|
||||||
pre: Value,
|
pre: TreeValues,
|
||||||
post: Value,
|
post: TreeValues,
|
||||||
children: Vec<Box<ScopeData>>,
|
children: Vec<Box<ScopeData>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScopeData {
|
impl ScopeData {
|
||||||
fn new(name: String, pre: Value) -> ScopeData {
|
fn new(name: String, box_tree: Value, fragment_tree: Value) -> ScopeData {
|
||||||
ScopeData {
|
ScopeData {
|
||||||
name: name,
|
name,
|
||||||
pre: pre,
|
pre: TreeValues {
|
||||||
post: Value::Null,
|
box_tree,
|
||||||
|
fragment_tree,
|
||||||
|
},
|
||||||
|
post: TreeValues {
|
||||||
|
box_tree: Value::Null,
|
||||||
|
fragment_tree: Value::Null,
|
||||||
|
},
|
||||||
children: vec![],
|
children: vec![],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
struct State {
|
||||||
fragment: Arc<FragmentTreeRoot>,
|
fragment_tree: Arc<FragmentTreeRoot>,
|
||||||
|
box_tree: Arc<BoxTreeRoot>,
|
||||||
scope_stack: Vec<Box<ScopeData>>,
|
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.
|
/// will be output at the beginning and end of this scope.
|
||||||
impl Scope {
|
impl Scope {
|
||||||
pub fn new(name: String) -> Scope {
|
pub fn new(name: String) -> Scope {
|
||||||
STATE_KEY.with(|ref r| {
|
STATE_KEY.with(|ref r| {
|
||||||
if let Some(ref mut state) = *r.borrow_mut() {
|
if let Some(ref mut state) = *r.borrow_mut() {
|
||||||
let fragment_tree = to_value(&state.fragment).unwrap();
|
let box_tree = to_value(&state.box_tree).unwrap();
|
||||||
let data = Box::new(ScopeData::new(name.clone(), fragment_tree));
|
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);
|
state.scope_stack.push(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -77,7 +91,10 @@ impl Drop for Scope {
|
||||||
STATE_KEY.with(|ref r| {
|
STATE_KEY.with(|ref r| {
|
||||||
if let Some(ref mut state) = *r.borrow_mut() {
|
if let Some(ref mut state) = *r.borrow_mut() {
|
||||||
let mut current_scope = state.scope_stack.pop().unwrap();
|
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();
|
let previous_scope = state.scope_stack.last_mut().unwrap();
|
||||||
previous_scope.children.push(current_scope);
|
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,
|
/// Begin a layout debug trace. If this has not been called,
|
||||||
/// creating debug scopes has no effect.
|
/// 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()));
|
assert!(STATE_KEY.with(|ref r| r.borrow().is_none()));
|
||||||
|
|
||||||
STATE_KEY.with(|ref r| {
|
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 {
|
let state = State {
|
||||||
scope_stack: vec![Box::new(ScopeData::new("root".to_owned(), root_trace))],
|
scope_stack: vec![Box::new(ScopeData::new(
|
||||||
fragment: root.clone(),
|
"root".to_owned(),
|
||||||
|
box_tree_value,
|
||||||
|
fragment_tree_value,
|
||||||
|
))],
|
||||||
|
box_tree,
|
||||||
|
fragment_tree,
|
||||||
};
|
};
|
||||||
*r.borrow_mut() = Some(state);
|
*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());
|
let mut thread_state = STATE_KEY.with(|ref r| r.borrow_mut().take().unwrap());
|
||||||
assert_eq!(thread_state.scope_stack.len(), 1);
|
assert_eq!(thread_state.scope_stack.len(), 1);
|
||||||
let mut root_scope = thread_state.scope_stack.pop().unwrap();
|
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 result = to_string(&root_scope).unwrap();
|
||||||
let mut file = File::create(format!("layout_trace-{}.json", generation)).unwrap();
|
let mut file = File::create(format!("layout_trace-{}.json", generation)).unwrap();
|
||||||
file.write_all(result.as_bytes()).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::values::computed::{Length, LengthOrAuto, LengthPercentage, LengthPercentageOrAuto};
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct AbsolutelyPositionedBox {
|
pub(crate) struct AbsolutelyPositionedBox {
|
||||||
pub contents: IndependentFormattingContext,
|
pub contents: IndependentFormattingContext,
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ use style::values::computed::{Length, LengthOrAuto};
|
||||||
use style::values::CSSFloat;
|
use style::values::CSSFloat;
|
||||||
use style::Zero;
|
use style::Zero;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) struct ReplacedContent {
|
pub(crate) struct ReplacedContent {
|
||||||
pub kind: ReplacedContentKind,
|
pub kind: ReplacedContentKind,
|
||||||
intrinsic: IntrinsicSizes,
|
intrinsic: IntrinsicSizes,
|
||||||
|
@ -41,7 +41,7 @@ pub(crate) struct IntrinsicSizes {
|
||||||
pub ratio: Option<CSSFloat>,
|
pub ratio: Option<CSSFloat>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) enum ReplacedContentKind {
|
pub(crate) enum ReplacedContentKind {
|
||||||
Image(Option<Arc<Image>>),
|
Image(Option<Arc<Image>>),
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ impl ContentSizesRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, Serialize)]
|
||||||
pub(crate) struct ContentSizes {
|
pub(crate) struct ContentSizes {
|
||||||
pub min_content: Length,
|
pub min_content: Length,
|
||||||
pub max_content: Length,
|
pub max_content: Length,
|
||||||
|
@ -83,7 +83,7 @@ impl ContentSizes {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optional min/max-content for storage in the box tree
|
/// Optional min/max-content for storage in the box tree
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Serialize)]
|
||||||
pub(crate) enum BoxContentSizes {
|
pub(crate) enum BoxContentSizes {
|
||||||
NoneWereRequested, // … during box construction
|
NoneWereRequested, // … during box construction
|
||||||
Inline(ContentSizes),
|
Inline(ContentSizes),
|
||||||
|
|
|
@ -173,7 +173,7 @@ pub struct LayoutThread {
|
||||||
outstanding_web_fonts: Arc<AtomicUsize>,
|
outstanding_web_fonts: Arc<AtomicUsize>,
|
||||||
|
|
||||||
/// The root of the box tree.
|
/// 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.
|
/// The root of the fragment tree.
|
||||||
fragment_tree_root: RefCell<Option<Arc<FragmentTreeRoot>>>,
|
fragment_tree_root: RefCell<Option<Arc<FragmentTreeRoot>>>,
|
||||||
|
@ -1154,7 +1154,8 @@ impl LayoutThread {
|
||||||
} else {
|
} else {
|
||||||
build_box_tree()
|
build_box_tree()
|
||||||
};
|
};
|
||||||
Some(box_tree)
|
|
||||||
|
Some(Arc::new(box_tree))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
@ -1167,13 +1168,13 @@ impl LayoutThread {
|
||||||
self.viewport_size.height.to_f32_px(),
|
self.viewport_size.height.to_f32_px(),
|
||||||
);
|
);
|
||||||
let run_layout = || box_tree.layout(&layout_context, viewport_size);
|
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)
|
pool.install(run_layout)
|
||||||
} else {
|
} else {
|
||||||
run_layout()
|
run_layout()
|
||||||
};
|
});
|
||||||
*self.box_tree_root.borrow_mut() = Some(box_tree);
|
*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 {
|
for element in elements_with_snapshot {
|
||||||
|
@ -1382,6 +1383,12 @@ impl LayoutThread {
|
||||||
document: Option<&ServoLayoutDocument>,
|
document: Option<&ServoLayoutDocument>,
|
||||||
context: &mut LayoutContext,
|
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() {
|
if !reflow_goal.needs_display() {
|
||||||
// Defer the paint step until the next ForDisplay.
|
// Defer the paint step until the next ForDisplay.
|
||||||
//
|
//
|
||||||
|
@ -1393,10 +1400,6 @@ impl LayoutThread {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.trace_layout {
|
|
||||||
layout_debug::begin_trace(fragment_tree.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(document) = document {
|
if let Some(document) = document {
|
||||||
document.will_paint();
|
document.will_paint();
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<div class='panel panel-default'>
|
||||||
|
<div class='panel-heading'>Box Tree</div>
|
||||||
|
<div class='panel-body' id="box-tree"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<div class='panel panel-default'>
|
<div class='panel panel-default'>
|
||||||
|
@ -90,10 +98,69 @@
|
||||||
<script src="js/formatters.min.js"></script>
|
<script src="js/formatters.min.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function create_fragment_tree(trace_node) {
|
function get_inner_boxes(box) {
|
||||||
var fragment = Object.values(trace_node)[0];
|
const box_type = Object.keys(box)[0];
|
||||||
|
switch (box_type) {
|
||||||
|
case "BlockLevelBoxes":
|
||||||
|
return box.BlockLevelBoxes;
|
||||||
|
case "InlineFormattingContext":
|
||||||
|
return box.InlineFormattingContext.inline_level_boxes;
|
||||||
|
case "InlineBox":
|
||||||
|
return box.InlineBox.children;
|
||||||
|
case "SameFormattingContextBlock":
|
||||||
|
case "Independent":
|
||||||
|
case "Flow":
|
||||||
|
case "OutOfFlowAbsolutelyPositionedBox":
|
||||||
|
case "OutOfFlowFloatBox":
|
||||||
|
case "Atomic":
|
||||||
|
return box[box_type].contents;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function box_tree_from_container(container) {
|
||||||
|
const box_type = Object.keys(container)[0];
|
||||||
|
let inner_boxes = get_inner_boxes(container);
|
||||||
|
let nodes = [];
|
||||||
|
let text = box_type;
|
||||||
|
if (Array.isArray(inner_boxes)) {
|
||||||
|
if (!inner_boxes.length) {
|
||||||
|
nodes = null;
|
||||||
|
} else {
|
||||||
|
for (let box in inner_boxes) {
|
||||||
|
nodes.push(box_tree_from_container(inner_boxes[box]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (inner_boxes != null) {
|
||||||
|
nodes.push(box_tree_from_container(inner_boxes));
|
||||||
|
} else {
|
||||||
|
if (box_type == "TextRun") {
|
||||||
|
text += ` (${container.TextRun.text})`;
|
||||||
|
}
|
||||||
|
nodes = null;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text,
|
||||||
|
nodes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function box_tree_from_bfc(bfc) {
|
||||||
|
const { contains_floats, contents } = bfc;
|
||||||
|
var block_container = Object.values(contents)[0];
|
||||||
|
return {
|
||||||
|
text: "BlockFormattingContext",
|
||||||
|
info: {
|
||||||
|
contains_floats,
|
||||||
|
},
|
||||||
|
nodes: [box_tree_from_container(contents)]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function create_fragment_tree(root) {
|
||||||
|
var fragment = Object.values(root)[0];
|
||||||
var node = {
|
var node = {
|
||||||
text: Object.keys(trace_node)[0],
|
text: Object.keys(root)[0],
|
||||||
id: fragment.debug_id,
|
id: fragment.debug_id,
|
||||||
icon: "dummy",
|
icon: "dummy",
|
||||||
href: "#diff-" + fragment.debug_id
|
href: "#diff-" + fragment.debug_id
|
||||||
|
@ -132,12 +199,13 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function flatten_trace(trace_node) {
|
function flatten_trace(trace_node) {
|
||||||
const node = Object.values(trace_node)[0];
|
const fragment_tree_root = Object.values(trace_node.fragment_tree)[0];
|
||||||
var fragments_info = {};
|
var fragments_info = {};
|
||||||
get_fragments_info(node, fragments_info);
|
get_fragments_info(fragment_tree_root, fragments_info);
|
||||||
return {
|
return {
|
||||||
tree: create_fragment_tree(node),
|
fragment_tree: create_fragment_tree(fragment_tree_root),
|
||||||
fragments_info: fragments_info,
|
box_tree: box_tree_from_bfc(trace_node.box_tree),
|
||||||
|
fragments_info,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -148,9 +216,12 @@
|
||||||
var tree_node = {
|
var tree_node = {
|
||||||
text: trace_node.name,
|
text: trace_node.name,
|
||||||
icon: "dummy",
|
icon: "dummy",
|
||||||
fragment_tree: pre_trace.tree, // assume pre/post trace always have same tree!
|
box_tree: pre_trace.box_tree,
|
||||||
pre: pre_trace.fragments_info,
|
fragment_tree: pre_trace.fragment_tree,
|
||||||
post: post_trace.fragments_info,
|
pre_boxes_info: pre_trace.boxes_info,
|
||||||
|
post_boxes_info: post_trace.boxes_info,
|
||||||
|
pre_fragments_info: pre_trace.fragments_info,
|
||||||
|
post_fragments_info: post_trace.fragments_info,
|
||||||
};
|
};
|
||||||
|
|
||||||
var trace_node = Object.values(trace_node)[0];
|
var trace_node = Object.values(trace_node)[0];
|
||||||
|
@ -169,7 +240,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_fragment_tree_bgcolor(fragment_tree_node, node_color_hash) {
|
function update_fragment_tree_bgcolor(fragment_tree_node, node_color_hash) {
|
||||||
fragment_tree_node.backColor = node_color_hash[fragment_tree_node.id];
|
fragment_tree_node.backColor = node_color_hash[fragment_tree_node.debug_id];
|
||||||
if (fragment_tree_node.nodes !== undefined) {
|
if (fragment_tree_node.nodes !== undefined) {
|
||||||
for (var i=0 ; i < fragment_tree_node.nodes.length ; ++i) {
|
for (var i=0 ; i < fragment_tree_node.nodes.length ; ++i) {
|
||||||
update_fragment_tree_bgcolor(fragment_tree_node.nodes[i], node_color_hash)
|
update_fragment_tree_bgcolor(fragment_tree_node.nodes[i], node_color_hash)
|
||||||
|
@ -187,9 +258,9 @@
|
||||||
$("#fragment-diffs").empty();
|
$("#fragment-diffs").empty();
|
||||||
$('#trace-tree').treeview(true).revealNode(node);
|
$('#trace-tree').treeview(true).revealNode(node);
|
||||||
|
|
||||||
for (var key in node.pre) {
|
for (var key in node.pre_fragments_info) {
|
||||||
var fragment_info_left = node.pre[key];
|
var fragment_info_left = node.pre_fragments_info[key];
|
||||||
var fragment_info_right = node.post[key];
|
var fragment_info_right = node.post_fragments_info[key];
|
||||||
|
|
||||||
var delta = jsondiffpatch.create({
|
var delta = jsondiffpatch.create({
|
||||||
objectHash: function(obj) {
|
objectHash: function(obj) {
|
||||||
|
@ -224,6 +295,7 @@
|
||||||
|
|
||||||
update_fragment_tree_bgcolor(node.fragment_tree, node_color_hash);
|
update_fragment_tree_bgcolor(node.fragment_tree, node_color_hash);
|
||||||
$('#fragment-tree').treeview({data: [node.fragment_tree], levels: 100, enableLinks: true, emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon"});
|
$('#fragment-tree').treeview({data: [node.fragment_tree], levels: 100, enableLinks: true, emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon"});
|
||||||
|
$('#box-tree').treeview({data: [node.box_tree], levels: 100, enableLinks: true, emptyIcon: "glyphicon glyphicon-unchecked hidden-glyphicon"});
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#trace-tree').treeview(true).selectNode(0);
|
$('#trace-tree').treeview(true).selectNode(0);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue