New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Layout viewer for layout 2020 #25803
Merged
+622
−43
Merged
Changes from 1 commit
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
5cbe053
Add layout debugger support to layout_2020
ferjm cce74e8
Ignore layout debugger trace files
ferjm 84dd334
Display fragment tree on layout viewer
ferjm 67706f9
Add fragment debug id
ferjm aaa3cd9
Display fragment tree info on layout viewer
ferjm a042f85
Dump box tree state into json files and display it on layout 2020 viewer
ferjm c4276aa
Fix rebase issues and run Prettier on layout viewer code
ferjm f81a2f0
Show box tree node info and clean up unused code
ferjm c33a517
Keep layout viewer versions for both 2013 and 2020 engines
ferjm 5e76c93
Minor improvements to layout_debug and associated code
ferjm File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.
Add layout debugger support to layout_2020
- Loading branch information
commit 5cbe05366bf11de3bb38e89a3d2ae2aacfadf2b6
Some generated files are not rendered by default. Learn more.
Oops, something went wrong.
| @@ -6,6 +6,7 @@ use crate::geom::flow_relative::{Rect, Sides, Vec2}; | ||
| use crate::geom::{PhysicalPoint, PhysicalRect}; | ||
| use gfx::text::glyph::GlyphStore; | ||
| use gfx_traits::print_tree::PrintTree; | ||
| use serde::ser::{Serialize, SerializeStruct, Serializer}; | ||
| use servo_arc::Arc as ServoArc; | ||
| use std::sync::Arc; | ||
| use style::computed_values::overflow_x::T as ComputedOverflow; | ||
| @@ -16,6 +17,7 @@ use style::values::computed::Length; | ||
| use style::Zero; | ||
| use webrender_api::{FontInstanceKey, ImageKey}; | ||
|
|
||
| #[derive(Serialize)] | ||
| pub(crate) enum Fragment { | ||
| Box(BoxFragment), | ||
| Anonymous(AnonymousFragment), | ||
| @@ -43,19 +45,21 @@ pub(crate) struct BoxFragment { | ||
| pub scrollable_overflow_from_children: PhysicalRect<Length>, | ||
| } | ||
|
|
||
| #[derive(Serialize)] | ||
| pub(crate) struct CollapsedBlockMargins { | ||
| pub collapsed_through: bool, | ||
| pub start: CollapsedMargin, | ||
| pub end: CollapsedMargin, | ||
| } | ||
|
|
||
| #[derive(Clone, Copy)] | ||
| #[derive(Clone, Copy, Serialize)] | ||
| pub(crate) struct CollapsedMargin { | ||
| max_positive: Length, | ||
| min_negative: Length, | ||
| } | ||
|
|
||
| /// Can contain child fragments with relative coordinates, but does not contribute to painting itself. | ||
| #[derive(Serialize)] | ||
| pub(crate) struct AnonymousFragment { | ||
| pub rect: Rect<Length>, | ||
| pub children: Vec<Fragment>, | ||
| @@ -342,3 +346,37 @@ impl CollapsedMargin { | ||
| self.max_positive + self.min_negative | ||
| } | ||
| } | ||
|
|
||
| impl Serialize for BoxFragment { | ||
| fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||
| let mut serializer = serializer.serialize_struct("BoxFragment", 6)?; | ||
| serializer.serialize_field("content_rect", &self.content_rect)?; | ||
| serializer.serialize_field("padding", &self.padding)?; | ||
| serializer.serialize_field("border", &self.border)?; | ||
| serializer.serialize_field("margin", &self.margin)?; | ||
| serializer.serialize_field( | ||
| "block_margins_collapsed_with_children", | ||
| &self.block_margins_collapsed_with_children, | ||
| )?; | ||
| serializer.serialize_field("children", &self.children)?; | ||
| serializer.end() | ||
| } | ||
| } | ||
|
|
||
| impl Serialize for TextFragment { | ||
| fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||
| let mut serializer = serializer.serialize_struct("TextFragment", 3)?; | ||
| serializer.serialize_field("rect", &self.rect)?; | ||
| serializer.serialize_field("ascent", &self.ascent)?; | ||
| serializer.serialize_field("glyphs", &self.glyphs)?; | ||
| serializer.end() | ||
| } | ||
| } | ||
|
|
||
| impl Serialize for ImageFragment { | ||
| fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||
| let mut serializer = serializer.serialize_struct("ImageFragment", 1)?; | ||
| serializer.serialize_field("rect", &self.rect)?; | ||
| serializer.end() | ||
| } | ||
| } | ||
|
This conversation was marked as resolved
by SimonSapin
ferjm
Author
Member
|
||
| @@ -0,0 +1,110 @@ | ||
| /* 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 https://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 crate::flow::FragmentTreeRoot; | ||
| use serde_json::{to_string, to_value, Value}; | ||
| use std::cell::RefCell; | ||
| use std::fs::File; | ||
| use std::io::Write; | ||
| use std::sync::Arc; | ||
|
|
||
| thread_local!(static STATE_KEY: RefCell<Option<State>> = RefCell::new(None)); | ||
|
|
||
| pub struct Scope; | ||
|
|
||
| #[macro_export] | ||
| macro_rules! layout_debug_scope( | ||
| ($($arg:tt)*) => ( | ||
| if cfg!(debug_assertions) { | ||
| layout_debug::Scope::new(format!($($arg)*)) | ||
| } else { | ||
| layout_debug::Scope | ||
| } | ||
| ) | ||
| ); | ||
|
|
||
| #[derive(Serialize)] | ||
| struct ScopeData { | ||
| name: String, | ||
| pre: Value, | ||
| post: Value, | ||
| children: Vec<Box<ScopeData>>, | ||
| } | ||
|
|
||
| impl ScopeData { | ||
| fn new(name: String, pre: Value) -> ScopeData { | ||
| ScopeData { | ||
| name: name, | ||
| pre: pre, | ||
| post: Value::Null, | ||
| children: vec![], | ||
| } | ||
| } | ||
| } | ||
|
|
||
| struct State { | ||
| fragment: Arc<FragmentTreeRoot>, | ||
| scope_stack: Vec<Box<ScopeData>>, | ||
| } | ||
|
|
||
| /// A layout debugging scope. The entire state of the fragment tree | ||
| /// 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)); | ||
| state.scope_stack.push(data); | ||
| } | ||
| }); | ||
| Scope | ||
| } | ||
| } | ||
|
|
||
| #[cfg(debug_assertions)] | ||
| impl Drop for Scope { | ||
| fn drop(&mut self) { | ||
| 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(); | ||
| let previous_scope = state.scope_stack.last_mut().unwrap(); | ||
| previous_scope.children.push(current_scope); | ||
| } | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| /// Begin a layout debug trace. If this has not been called, | ||
| /// creating debug scopes has no effect. | ||
| pub fn begin_trace(root: 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 state = State { | ||
| scope_stack: vec![Box::new(ScopeData::new("root".to_owned(), root_trace))], | ||
| fragment: root.clone(), | ||
| }; | ||
| *r.borrow_mut() = Some(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(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(); | ||
|
|
||
| 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(); | ||
|
Comment on lines
+108
to
+109
|
||
| } | ||
ProTip!
Use n and p to navigate between commits in a pull request.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
For these three impls: why are they not derived? Could they be derived with some attributes from https://serde.rs/field-attrs.html ?