mirror of
https://github.com/servo/servo.git
synced 2025-08-02 20:20:14 +01:00
Implements reflow events debugging.
Start servo with -Z relayout-event and you should have reflow events printed to the terminal.
This commit is contained in:
parent
34289943e3
commit
618f56410d
4 changed files with 70 additions and 18 deletions
|
@ -56,7 +56,7 @@ use dom::processinginstruction::ProcessingInstruction;
|
||||||
use dom::range::Range;
|
use dom::range::Range;
|
||||||
use dom::treewalker::TreeWalker;
|
use dom::treewalker::TreeWalker;
|
||||||
use dom::uievent::UIEvent;
|
use dom::uievent::UIEvent;
|
||||||
use dom::window::{Window, WindowHelpers};
|
use dom::window::{Window, WindowHelpers, ReflowReason};
|
||||||
|
|
||||||
use layout_interface::{HitTestResponse, MouseOverResponse};
|
use layout_interface::{HitTestResponse, MouseOverResponse};
|
||||||
use msg::compositor_msg::ScriptListener;
|
use msg::compositor_msg::ScriptListener;
|
||||||
|
@ -523,7 +523,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
|
||||||
el.authentic_click_activation(event);
|
el.authentic_click_activation(event);
|
||||||
|
|
||||||
self.commit_focus_transaction();
|
self.commit_focus_transaction();
|
||||||
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery);
|
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::MouseEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return need force reflow or not
|
/// Return need force reflow or not
|
||||||
|
@ -664,7 +664,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
|
||||||
_ => ()
|
_ => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery);
|
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::KeyEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_current_script(self, script: Option<JSRef<HTMLScriptElement>>) {
|
fn set_current_script(self, script: Option<JSRef<HTMLScriptElement>>) {
|
||||||
|
|
|
@ -41,6 +41,7 @@ use net::image_cache_task::ImageCacheTask;
|
||||||
use net::resource_task::ResourceTask;
|
use net::resource_task::ResourceTask;
|
||||||
use net::storage_task::StorageTask;
|
use net::storage_task::StorageTask;
|
||||||
use util::geometry::{self, Au, MAX_RECT};
|
use util::geometry::{self, Au, MAX_RECT};
|
||||||
|
use util::opts;
|
||||||
use util::str::{DOMString,HTML_SPACE_CHARACTERS};
|
use util::str::{DOMString,HTML_SPACE_CHARACTERS};
|
||||||
|
|
||||||
use geom::{Point2D, Rect, Size2D};
|
use geom::{Point2D, Rect, Size2D};
|
||||||
|
@ -63,6 +64,19 @@ use std::sync::mpsc::{channel, Receiver};
|
||||||
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
|
use std::sync::mpsc::TryRecvError::{Empty, Disconnected};
|
||||||
use time;
|
use time;
|
||||||
|
|
||||||
|
/// Extra information concerning the reason for reflowing.
|
||||||
|
pub enum ReflowReason {
|
||||||
|
CachedPageNeededReflow,
|
||||||
|
FirstLoad,
|
||||||
|
KeyEvent,
|
||||||
|
MouseEvent,
|
||||||
|
Query,
|
||||||
|
ReceivedReflowEvent,
|
||||||
|
Timer,
|
||||||
|
Viewport,
|
||||||
|
WindowResize,
|
||||||
|
}
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
eventtarget: EventTarget,
|
eventtarget: EventTarget,
|
||||||
|
@ -397,7 +411,7 @@ pub trait WindowHelpers {
|
||||||
fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>);
|
fn init_browser_context(self, doc: JSRef<Document>, frame_element: Option<JSRef<Element>>);
|
||||||
fn load_url(self, href: DOMString);
|
fn load_url(self, href: DOMString);
|
||||||
fn handle_fire_timer(self, timer_id: TimerId);
|
fn handle_fire_timer(self, timer_id: TimerId);
|
||||||
fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType);
|
fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason);
|
||||||
fn join_layout(self);
|
fn join_layout(self);
|
||||||
fn layout(&self) -> &LayoutRPC;
|
fn layout(&self) -> &LayoutRPC;
|
||||||
fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au>;
|
fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au>;
|
||||||
|
@ -480,7 +494,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
||||||
/// yet, the page is presumed invisible and no reflow is performed.
|
/// yet, the page is presumed invisible and no reflow is performed.
|
||||||
///
|
///
|
||||||
/// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout.
|
/// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout.
|
||||||
fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType) {
|
fn reflow(self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
|
||||||
let document = self.Document().root();
|
let document = self.Document().root();
|
||||||
let root = document.r().GetDocumentElement().root();
|
let root = document.r().GetDocumentElement().root();
|
||||||
let root = match root.r() {
|
let root = match root.r() {
|
||||||
|
@ -511,6 +525,11 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
||||||
|
|
||||||
let window_size = self.window_size.get();
|
let window_size = self.window_size.get();
|
||||||
|
|
||||||
|
// On debug mode, print the reflow event information.
|
||||||
|
if opts::get().relayout_event {
|
||||||
|
debug_reflow_events(&goal, &query_type, &reason);
|
||||||
|
}
|
||||||
|
|
||||||
// Send new document and relevant styles to layout.
|
// Send new document and relevant styles to layout.
|
||||||
let reflow = box Reflow {
|
let reflow = box Reflow {
|
||||||
document_root: root.to_trusted_node_address(),
|
document_root: root.to_trusted_node_address(),
|
||||||
|
@ -562,14 +581,14 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
|
fn content_box_query(self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request));
|
self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxQuery(content_box_request), ReflowReason::Query);
|
||||||
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
||||||
let ContentBoxResponse(rect) = self.layout_rpc.content_box();
|
let ContentBoxResponse(rect) = self.layout_rpc.content_box();
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
|
fn content_boxes_query(self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request));
|
self.reflow(ReflowGoal::ForScriptQuery, ReflowQueryType::ContentBoxesQuery(content_boxes_request), ReflowReason::Query);
|
||||||
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
self.join_layout(); //FIXME: is this necessary, or is layout_rpc's mutex good enough?
|
||||||
let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
|
let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
|
||||||
rects
|
rects
|
||||||
|
@ -609,7 +628,7 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
||||||
|
|
||||||
fn handle_fire_timer(self, timer_id: TimerId) {
|
fn handle_fire_timer(self, timer_id: TimerId) {
|
||||||
self.timers.fire_timer(timer_id, self);
|
self.timers.fire_timer(timer_id, self);
|
||||||
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery);
|
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::Timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_fragment_name(self, fragment: Option<String>) {
|
fn set_fragment_name(self, fragment: Option<String>) {
|
||||||
|
@ -715,7 +734,6 @@ impl<'a> WindowHelpers for JSRef<'a, Window> {
|
||||||
fn freeze(self) {
|
fn freeze(self) {
|
||||||
self.timers.suspend();
|
self.timers.suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Window {
|
impl Window {
|
||||||
|
@ -798,3 +816,31 @@ fn should_move_clip_rect(clip_rect: Rect<Au>, new_viewport: Rect<f32>) -> bool{
|
||||||
(clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
|
(clip_rect.origin.y - new_viewport.origin.y).abs() <= viewport_scroll_margin.height ||
|
||||||
(clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
|
(clip_rect.max_y() - new_viewport.max_y()).abs() <= viewport_scroll_margin.height
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn debug_reflow_events(goal: &ReflowGoal, query_type: &ReflowQueryType, reason: &ReflowReason) {
|
||||||
|
let mut debug_msg = String::from_str("****");
|
||||||
|
match *goal {
|
||||||
|
ReflowGoal::ForDisplay => debug_msg.push_str("\tForDisplay"),
|
||||||
|
ReflowGoal::ForScriptQuery => debug_msg.push_str("\tForScriptQuery"),
|
||||||
|
}
|
||||||
|
|
||||||
|
match *query_type {
|
||||||
|
ReflowQueryType::NoQuery => debug_msg.push_str("\tNoQuery"),
|
||||||
|
ReflowQueryType::ContentBoxQuery(_n) => debug_msg.push_str("\tContentBoxQuery"),
|
||||||
|
ReflowQueryType::ContentBoxesQuery(_n) => debug_msg.push_str("\tContentBoxesQuery"),
|
||||||
|
}
|
||||||
|
|
||||||
|
match *reason {
|
||||||
|
ReflowReason::CachedPageNeededReflow => debug_msg.push_str("\tCachedPageNeededReflow"),
|
||||||
|
ReflowReason::FirstLoad => debug_msg.push_str("\tFirstLoad"),
|
||||||
|
ReflowReason::KeyEvent => debug_msg.push_str("\tKeyEvent"),
|
||||||
|
ReflowReason::MouseEvent => debug_msg.push_str("\tMouseEvent"),
|
||||||
|
ReflowReason::Query => debug_msg.push_str("\tQuery"),
|
||||||
|
ReflowReason::ReceivedReflowEvent => debug_msg.push_str("\tReceivedReflowEvent"),
|
||||||
|
ReflowReason::Timer => debug_msg.push_str("\tTimer"),
|
||||||
|
ReflowReason::Viewport => debug_msg.push_str("\tViewport"),
|
||||||
|
ReflowReason::WindowResize => debug_msg.push_str("\tWindowResize"),
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("{}", debug_msg);
|
||||||
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ use dom::event::{Event, EventHelpers};
|
||||||
use dom::uievent::UIEvent;
|
use dom::uievent::UIEvent;
|
||||||
use dom::eventtarget::EventTarget;
|
use dom::eventtarget::EventTarget;
|
||||||
use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node};
|
use dom::node::{self, Node, NodeHelpers, NodeDamage, window_from_node};
|
||||||
use dom::window::{Window, WindowHelpers, ScriptHelpers};
|
use dom::window::{Window, WindowHelpers, ScriptHelpers, ReflowReason};
|
||||||
use dom::worker::{Worker, TrustedWorkerAddress};
|
use dom::worker::{Worker, TrustedWorkerAddress};
|
||||||
use parse::html::{HTMLInput, parse_html};
|
use parse::html::{HTMLInput, parse_html};
|
||||||
use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowGoal, ReflowQueryType};
|
use layout_interface::{ScriptLayoutChan, LayoutChan, ReflowGoal, ReflowQueryType};
|
||||||
|
@ -697,7 +697,7 @@ impl ScriptTask {
|
||||||
let window = inner_page.window().root();
|
let window = inner_page.window().root();
|
||||||
if window.r().set_page_clip_rect_with_new_viewport(rect) {
|
if window.r().set_page_clip_rect_with_new_viewport(rect) {
|
||||||
let page = get_page(page, id);
|
let page = get_page(page, id);
|
||||||
self.force_reflow(&*page);
|
self.force_reflow(&*page, ReflowReason::Viewport);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -771,7 +771,7 @@ impl ScriptTask {
|
||||||
|
|
||||||
let needed_reflow = page.set_reflow_status(false);
|
let needed_reflow = page.set_reflow_status(false);
|
||||||
if needed_reflow {
|
if needed_reflow {
|
||||||
self.force_reflow(&*page);
|
self.force_reflow(&*page, ReflowReason::CachedPageNeededReflow);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1007,7 +1007,7 @@ impl ScriptTask {
|
||||||
debug!("kicking off initial reflow of {:?}", final_url);
|
debug!("kicking off initial reflow of {:?}", final_url);
|
||||||
document.r().content_changed(NodeCast::from_ref(document.r()),
|
document.r().content_changed(NodeCast::from_ref(document.r()),
|
||||||
NodeDamage::OtherNodeDamage);
|
NodeDamage::OtherNodeDamage);
|
||||||
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery);
|
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::FirstLoad);
|
||||||
|
|
||||||
// No more reflow required
|
// No more reflow required
|
||||||
page.set_reflow_status(false);
|
page.set_reflow_status(false);
|
||||||
|
@ -1060,11 +1060,11 @@ impl ScriptTask {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflows non-incrementally.
|
/// Reflows non-incrementally.
|
||||||
fn force_reflow(&self, page: &Page) {
|
fn force_reflow(&self, page: &Page, reason: ReflowReason) {
|
||||||
let document = page.document().root();
|
let document = page.document().root();
|
||||||
document.r().dirty_all_nodes();
|
document.r().dirty_all_nodes();
|
||||||
let window = window_from_node(document.r()).root();
|
let window = window_from_node(document.r()).root();
|
||||||
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery);
|
window.r().reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is the main entry point for receiving and dispatching DOM events.
|
/// This is the main entry point for receiving and dispatching DOM events.
|
||||||
|
@ -1113,7 +1113,7 @@ impl ScriptTask {
|
||||||
let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut();
|
let mouse_over_targets = &mut *self.mouse_over_targets.borrow_mut();
|
||||||
|
|
||||||
if document.r().handle_mouse_move_event(self.js_runtime.ptr, point, mouse_over_targets) {
|
if document.r().handle_mouse_move_event(self.js_runtime.ptr, point, mouse_over_targets) {
|
||||||
self.force_reflow(&page)
|
self.force_reflow(&page, ReflowReason::MouseEvent)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1151,7 +1151,7 @@ impl ScriptTask {
|
||||||
let page = get_page(&self.root_page(), pipeline_id);
|
let page = get_page(&self.root_page(), pipeline_id);
|
||||||
let window = page.window().root();
|
let window = page.window().root();
|
||||||
window.r().set_window_size(new_size);
|
window.r().set_window_size(new_size);
|
||||||
self.force_reflow(&*page);
|
self.force_reflow(&*page, ReflowReason::WindowResize);
|
||||||
|
|
||||||
let document = page.document().root();
|
let document = page.document().root();
|
||||||
let fragment_node = window.r().steal_fragment_name()
|
let fragment_node = window.r().steal_fragment_name()
|
||||||
|
@ -1177,7 +1177,7 @@ impl ScriptTask {
|
||||||
fn handle_reflow_event(&self, pipeline_id: PipelineId) {
|
fn handle_reflow_event(&self, pipeline_id: PipelineId) {
|
||||||
debug!("script got reflow event");
|
debug!("script got reflow event");
|
||||||
let page = get_page(&self.root_page(), pipeline_id);
|
let page = get_page(&self.root_page(), pipeline_id);
|
||||||
self.force_reflow(&*page);
|
self.force_reflow(&*page, ReflowReason::ReceivedReflowEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initiate a non-blocking fetch for a specified resource. Stores the InProgressLoad
|
/// Initiate a non-blocking fetch for a specified resource. Stores the InProgressLoad
|
||||||
|
|
|
@ -113,6 +113,9 @@ pub struct Opts {
|
||||||
/// Dumps the flow tree after a layout.
|
/// Dumps the flow tree after a layout.
|
||||||
pub dump_display_list: bool,
|
pub dump_display_list: bool,
|
||||||
|
|
||||||
|
/// Emits notifications when there is a relayout.
|
||||||
|
pub relayout_event: bool,
|
||||||
|
|
||||||
/// Whether to show an error when display list geometry escapes flow overflow regions.
|
/// Whether to show an error when display list geometry escapes flow overflow regions.
|
||||||
pub validate_display_list_geometry: bool,
|
pub validate_display_list_geometry: bool,
|
||||||
|
|
||||||
|
@ -136,6 +139,7 @@ pub fn print_debug_usage(app: &str) {
|
||||||
print_option("disable-text-aa", "Disable antialiasing of rendered text.");
|
print_option("disable-text-aa", "Disable antialiasing of rendered text.");
|
||||||
print_option("dump-flow-tree", "Print the flow tree after each layout.");
|
print_option("dump-flow-tree", "Print the flow tree after each layout.");
|
||||||
print_option("dump-display-list", "Print the display list after each layout.");
|
print_option("dump-display-list", "Print the display list after each layout.");
|
||||||
|
print_option("relayout-event", "Print notifications when there is a relayout.");
|
||||||
print_option("profile-tasks", "Instrument each task, writing the output to a file.");
|
print_option("profile-tasks", "Instrument each task, writing the output to a file.");
|
||||||
print_option("show-compositor-borders", "Paint borders along layer and tile boundaries.");
|
print_option("show-compositor-borders", "Paint borders along layer and tile boundaries.");
|
||||||
print_option("show-fragment-borders", "Paint borders along fragment boundaries.");
|
print_option("show-fragment-borders", "Paint borders along fragment boundaries.");
|
||||||
|
@ -188,6 +192,7 @@ pub fn default_opts() -> Opts {
|
||||||
user_agent: None,
|
user_agent: None,
|
||||||
dump_flow_tree: false,
|
dump_flow_tree: false,
|
||||||
dump_display_list: false,
|
dump_display_list: false,
|
||||||
|
relayout_event: false,
|
||||||
validate_display_list_geometry: false,
|
validate_display_list_geometry: false,
|
||||||
profile_tasks: false,
|
profile_tasks: false,
|
||||||
resources_path: None,
|
resources_path: None,
|
||||||
|
@ -336,6 +341,7 @@ pub fn from_cmdline_args(args: &[String]) -> bool {
|
||||||
enable_text_antialiasing: !debug_options.contains(&"disable-text-aa"),
|
enable_text_antialiasing: !debug_options.contains(&"disable-text-aa"),
|
||||||
dump_flow_tree: debug_options.contains(&"dump-flow-tree"),
|
dump_flow_tree: debug_options.contains(&"dump-flow-tree"),
|
||||||
dump_display_list: debug_options.contains(&"dump-display-list"),
|
dump_display_list: debug_options.contains(&"dump-display-list"),
|
||||||
|
relayout_event: debug_options.contains(&"relayout-event"),
|
||||||
validate_display_list_geometry: debug_options.contains(&"validate-display-list-geometry"),
|
validate_display_list_geometry: debug_options.contains(&"validate-display-list-geometry"),
|
||||||
resources_path: opt_match.opt_str("resources-path"),
|
resources_path: opt_match.opt_str("resources-path"),
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue