mirror of
https://github.com/servo/servo.git
synced 2025-06-09 00:53:26 +00:00
layout: Keep track of whether we've deferred the painting of the document due to
a script query. This will, rather unfortunately, mean that we might repaint two times if we've deferred a paint, then get an out-of-band reflow. Still seemed better than not suppressing paints at all. Fixes #13131
This commit is contained in:
parent
1fcc447941
commit
fd9cd2f103
6 changed files with 106 additions and 52 deletions
|
@ -85,7 +85,7 @@ use net_traits::image_cache_thread::{ImageCacheChan, ImageCacheResult, ImageCach
|
||||||
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
|
use profile_traits::mem::{self, Report, ReportKind, ReportsChan};
|
||||||
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
|
use profile_traits::time::{TimerMetadataFrameType, TimerMetadataReflowType};
|
||||||
use profile_traits::time::{self, TimerMetadata, profile};
|
use profile_traits::time::{self, TimerMetadata, profile};
|
||||||
use script::layout_wrapper::ServoLayoutNode;
|
use script::layout_wrapper::{ServoLayoutDocument, ServoLayoutNode};
|
||||||
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
|
use script_layout_interface::message::{Msg, NewLayoutThreadInfo, Reflow, ReflowQueryType, ScriptReflow};
|
||||||
use script_layout_interface::reporter::CSSErrorReporter;
|
use script_layout_interface::reporter::CSSErrorReporter;
|
||||||
use script_layout_interface::restyle_damage::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW};
|
use script_layout_interface::restyle_damage::{REPAINT, STORE_OVERFLOW, REFLOW_OUT_OF_FLOW, REFLOW};
|
||||||
|
@ -629,6 +629,7 @@ impl LayoutThread {
|
||||||
reflow_info.goal);
|
reflow_info.goal);
|
||||||
|
|
||||||
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
||||||
|
None,
|
||||||
&mut *rw_data,
|
&mut *rw_data,
|
||||||
&mut layout_context);
|
&mut layout_context);
|
||||||
|
|
||||||
|
@ -901,6 +902,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
fn compute_abs_pos_and_build_display_list(&mut self,
|
fn compute_abs_pos_and_build_display_list(&mut self,
|
||||||
data: &Reflow,
|
data: &Reflow,
|
||||||
|
document: Option<&ServoLayoutDocument>,
|
||||||
layout_root: &mut Flow,
|
layout_root: &mut Flow,
|
||||||
shared_layout_context: &mut SharedLayoutContext,
|
shared_layout_context: &mut SharedLayoutContext,
|
||||||
rw_data: &mut LayoutThreadData) {
|
rw_data: &mut LayoutThreadData) {
|
||||||
|
@ -962,59 +964,69 @@ impl LayoutThread {
|
||||||
Some(Arc::new(DisplayList::new(root_stacking_context, display_list_entries)))
|
Some(Arc::new(DisplayList::new(root_stacking_context, display_list_entries)))
|
||||||
}
|
}
|
||||||
|
|
||||||
if data.goal == ReflowGoal::ForDisplay {
|
if data.goal != ReflowGoal::ForDisplay {
|
||||||
let display_list = (*rw_data.display_list.as_ref().unwrap()).clone();
|
// Defer the paint step until the next ForDisplay.
|
||||||
|
//
|
||||||
|
// We need to tell the document about this so it doesn't
|
||||||
|
// incorrectly suppress reflows. See #13131.
|
||||||
|
document.expect("No document in a non-display reflow?")
|
||||||
|
.needs_paint_from_layout();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Some(document) = document {
|
||||||
|
document.will_paint();
|
||||||
|
}
|
||||||
|
let display_list = (*rw_data.display_list.as_ref().unwrap()).clone();
|
||||||
|
|
||||||
if opts::get().dump_display_list {
|
if opts::get().dump_display_list {
|
||||||
display_list.print();
|
display_list.print();
|
||||||
}
|
}
|
||||||
if opts::get().dump_display_list_json {
|
if opts::get().dump_display_list_json {
|
||||||
println!("{}", serde_json::to_string_pretty(&display_list).unwrap());
|
println!("{}", serde_json::to_string_pretty(&display_list).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("Layout done!");
|
debug!("Layout done!");
|
||||||
|
|
||||||
self.epoch.next();
|
self.epoch.next();
|
||||||
|
|
||||||
if let Some(ref mut webrender_api) = self.webrender_api {
|
if let Some(ref mut webrender_api) = self.webrender_api {
|
||||||
// TODO: Avoid the temporary conversion and build webrender sc/dl directly!
|
// TODO: Avoid the temporary conversion and build webrender sc/dl directly!
|
||||||
let Epoch(epoch_number) = self.epoch;
|
let Epoch(epoch_number) = self.epoch;
|
||||||
let epoch = webrender_traits::Epoch(epoch_number);
|
let epoch = webrender_traits::Epoch(epoch_number);
|
||||||
let pipeline_id = self.id.to_webrender();
|
let pipeline_id = self.id.to_webrender();
|
||||||
|
|
||||||
// TODO(gw) For now only create a root scrolling layer!
|
// TODO(gw) For now only create a root scrolling layer!
|
||||||
let mut frame_builder = WebRenderFrameBuilder::new(pipeline_id);
|
let mut frame_builder = WebRenderFrameBuilder::new(pipeline_id);
|
||||||
let root_scroll_layer_id = frame_builder.next_scroll_layer_id();
|
let root_scroll_layer_id = frame_builder.next_scroll_layer_id();
|
||||||
let sc_id = rw_data.display_list.as_ref().unwrap().convert_to_webrender(
|
let sc_id = rw_data.display_list.as_ref().unwrap().convert_to_webrender(
|
||||||
webrender_api,
|
webrender_api,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
epoch,
|
epoch,
|
||||||
Some(root_scroll_layer_id),
|
Some(root_scroll_layer_id),
|
||||||
&mut frame_builder);
|
&mut frame_builder);
|
||||||
let root_background_color = get_root_flow_background_color(layout_root);
|
let root_background_color = get_root_flow_background_color(layout_root);
|
||||||
let root_background_color =
|
let root_background_color =
|
||||||
webrender_traits::ColorF::new(root_background_color.r,
|
webrender_traits::ColorF::new(root_background_color.r,
|
||||||
root_background_color.g,
|
root_background_color.g,
|
||||||
root_background_color.b,
|
root_background_color.b,
|
||||||
root_background_color.a);
|
root_background_color.a);
|
||||||
|
|
||||||
let viewport_size = Size2D::new(self.viewport_size.width.to_f32_px(),
|
let viewport_size = Size2D::new(self.viewport_size.width.to_f32_px(),
|
||||||
self.viewport_size.height.to_f32_px());
|
self.viewport_size.height.to_f32_px());
|
||||||
|
|
||||||
webrender_api.set_root_stacking_context(
|
webrender_api.set_root_stacking_context(
|
||||||
sc_id,
|
sc_id,
|
||||||
root_background_color,
|
root_background_color,
|
||||||
epoch,
|
epoch,
|
||||||
pipeline_id,
|
pipeline_id,
|
||||||
viewport_size,
|
viewport_size,
|
||||||
frame_builder.stacking_contexts,
|
frame_builder.stacking_contexts,
|
||||||
frame_builder.display_lists,
|
frame_builder.display_lists,
|
||||||
frame_builder.auxiliary_lists_builder.finalize());
|
frame_builder.auxiliary_lists_builder.finalize());
|
||||||
} else {
|
} else {
|
||||||
self.paint_chan
|
self.paint_chan
|
||||||
.send(LayoutToPaintMsg::PaintInit(self.epoch, display_list))
|
.send(LayoutToPaintMsg::PaintInit(self.epoch, display_list))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1205,6 +1217,7 @@ impl LayoutThread {
|
||||||
|
|
||||||
// Perform post-style recalculation layout passes.
|
// Perform post-style recalculation layout passes.
|
||||||
self.perform_post_style_recalc_layout_passes(&data.reflow_info,
|
self.perform_post_style_recalc_layout_passes(&data.reflow_info,
|
||||||
|
Some(&document),
|
||||||
&mut rw_data,
|
&mut rw_data,
|
||||||
&mut shared_layout_context);
|
&mut shared_layout_context);
|
||||||
|
|
||||||
|
@ -1327,7 +1340,7 @@ impl LayoutThread {
|
||||||
false,
|
false,
|
||||||
reflow_info.goal);
|
reflow_info.goal);
|
||||||
|
|
||||||
self.perform_post_main_layout_passes(&reflow_info, &mut *rw_data, &mut layout_context);
|
self.perform_post_main_layout_passes(&reflow_info, None, &mut *rw_data, &mut layout_context);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1385,6 +1398,7 @@ impl LayoutThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
||||||
|
None,
|
||||||
&mut *rw_data,
|
&mut *rw_data,
|
||||||
&mut layout_context);
|
&mut layout_context);
|
||||||
}
|
}
|
||||||
|
@ -1407,12 +1421,14 @@ impl LayoutThread {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
self.perform_post_style_recalc_layout_passes(&reflow_info,
|
||||||
|
None,
|
||||||
&mut *rw_data,
|
&mut *rw_data,
|
||||||
&mut layout_context);
|
&mut layout_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_post_style_recalc_layout_passes(&mut self,
|
fn perform_post_style_recalc_layout_passes(&mut self,
|
||||||
data: &Reflow,
|
data: &Reflow,
|
||||||
|
document: Option<&ServoLayoutDocument>,
|
||||||
rw_data: &mut LayoutThreadData,
|
rw_data: &mut LayoutThreadData,
|
||||||
layout_context: &mut SharedLayoutContext) {
|
layout_context: &mut SharedLayoutContext) {
|
||||||
if let Some(mut root_flow) = self.root_flow.clone() {
|
if let Some(mut root_flow) = self.root_flow.clone() {
|
||||||
|
@ -1487,17 +1503,19 @@ impl LayoutThread {
|
||||||
flow_ref::deref_mut(&mut root_flow) as &mut Flow);
|
flow_ref::deref_mut(&mut root_flow) as &mut Flow);
|
||||||
});
|
});
|
||||||
|
|
||||||
self.perform_post_main_layout_passes(data, rw_data, layout_context);
|
self.perform_post_main_layout_passes(data, document, rw_data, layout_context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn perform_post_main_layout_passes(&mut self,
|
fn perform_post_main_layout_passes(&mut self,
|
||||||
data: &Reflow,
|
data: &Reflow,
|
||||||
|
document: Option<&ServoLayoutDocument>,
|
||||||
rw_data: &mut LayoutThreadData,
|
rw_data: &mut LayoutThreadData,
|
||||||
layout_context: &mut SharedLayoutContext) {
|
layout_context: &mut SharedLayoutContext) {
|
||||||
// Build the display list if necessary, and send it to the painter.
|
// Build the display list if necessary, and send it to the painter.
|
||||||
if let Some(mut root_flow) = self.root_flow.clone() {
|
if let Some(mut root_flow) = self.root_flow.clone() {
|
||||||
self.compute_abs_pos_and_build_display_list(data,
|
self.compute_abs_pos_and_build_display_list(data,
|
||||||
|
document,
|
||||||
flow_ref::deref_mut(&mut root_flow),
|
flow_ref::deref_mut(&mut root_flow),
|
||||||
&mut *layout_context,
|
&mut *layout_context,
|
||||||
rw_data);
|
rw_data);
|
||||||
|
|
|
@ -221,6 +221,9 @@ pub struct Document {
|
||||||
/// For each element that has had a state or attribute change since the last restyle,
|
/// For each element that has had a state or attribute change since the last restyle,
|
||||||
/// track the original condition of the element.
|
/// track the original condition of the element.
|
||||||
modified_elements: DOMRefCell<HashMap<JS<Element>, ElementSnapshot>>,
|
modified_elements: DOMRefCell<HashMap<JS<Element>, ElementSnapshot>>,
|
||||||
|
/// This flag will be true if layout suppressed a reflow attempt that was
|
||||||
|
/// needed in order for the page to be painted.
|
||||||
|
needs_paint: Cell<bool>,
|
||||||
/// http://w3c.github.io/touch-events/#dfn-active-touch-point
|
/// http://w3c.github.io/touch-events/#dfn-active-touch-point
|
||||||
active_touch_points: DOMRefCell<Vec<JS<Touch>>>,
|
active_touch_points: DOMRefCell<Vec<JS<Touch>>>,
|
||||||
/// Navigation Timing properties:
|
/// Navigation Timing properties:
|
||||||
|
@ -376,6 +379,10 @@ impl Document {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn needs_paint(&self) -> bool {
|
||||||
|
self.needs_paint.get()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn needs_reflow(&self) -> bool {
|
pub fn needs_reflow(&self) -> bool {
|
||||||
// FIXME: This should check the dirty bit on the document,
|
// FIXME: This should check the dirty bit on the document,
|
||||||
// not the document element. Needs some layout changes to make
|
// not the document element. Needs some layout changes to make
|
||||||
|
@ -384,7 +391,8 @@ impl Document {
|
||||||
Some(root) => {
|
Some(root) => {
|
||||||
root.upcast::<Node>().is_dirty() ||
|
root.upcast::<Node>().is_dirty() ||
|
||||||
root.upcast::<Node>().has_dirty_descendants() ||
|
root.upcast::<Node>().has_dirty_descendants() ||
|
||||||
!self.modified_elements.borrow().is_empty()
|
!self.modified_elements.borrow().is_empty() ||
|
||||||
|
self.needs_paint()
|
||||||
}
|
}
|
||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
|
@ -1602,6 +1610,8 @@ pub enum DocumentSource {
|
||||||
pub trait LayoutDocumentHelpers {
|
pub trait LayoutDocumentHelpers {
|
||||||
unsafe fn is_html_document_for_layout(&self) -> bool;
|
unsafe fn is_html_document_for_layout(&self) -> bool;
|
||||||
unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, ElementSnapshot)>;
|
unsafe fn drain_modified_elements(&self) -> Vec<(LayoutJS<Element>, ElementSnapshot)>;
|
||||||
|
unsafe fn needs_paint_from_layout(&self);
|
||||||
|
unsafe fn will_paint(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
|
@ -1618,6 +1628,16 @@ impl LayoutDocumentHelpers for LayoutJS<Document> {
|
||||||
let result = elements.drain().map(|(k, v)| (k.to_layout(), v)).collect();
|
let result = elements.drain().map(|(k, v)| (k.to_layout(), v)).collect();
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn needs_paint_from_layout(&self) {
|
||||||
|
(*self.unsafe_get()).needs_paint.set(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
unsafe fn will_paint(&self) {
|
||||||
|
(*self.unsafe_get()).needs_paint.set(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// https://url.spec.whatwg.org/#network-scheme
|
/// https://url.spec.whatwg.org/#network-scheme
|
||||||
|
@ -1723,6 +1743,7 @@ impl Document {
|
||||||
base_element: Default::default(),
|
base_element: Default::default(),
|
||||||
appropriate_template_contents_owner_document: Default::default(),
|
appropriate_template_contents_owner_document: Default::default(),
|
||||||
modified_elements: DOMRefCell::new(HashMap::new()),
|
modified_elements: DOMRefCell::new(HashMap::new()),
|
||||||
|
needs_paint: Cell::new(false),
|
||||||
active_touch_points: DOMRefCell::new(Vec::new()),
|
active_touch_points: DOMRefCell::new(Vec::new()),
|
||||||
dom_loading: Cell::new(Default::default()),
|
dom_loading: Cell::new(Default::default()),
|
||||||
dom_interactive: Cell::new(Default::default()),
|
dom_interactive: Cell::new(Default::default()),
|
||||||
|
|
|
@ -1200,9 +1200,11 @@ impl Window {
|
||||||
if !for_display || self.Document().needs_reflow() {
|
if !for_display || self.Document().needs_reflow() {
|
||||||
issued_reflow = self.force_reflow(goal, query_type, reason);
|
issued_reflow = self.force_reflow(goal, query_type, reason);
|
||||||
|
|
||||||
// If window_size is `None`, we don't reflow, so the document stays dirty.
|
// If window_size is `None`, we don't reflow, so the document stays
|
||||||
// Otherwise, we shouldn't need a reflow immediately after a reflow.
|
// dirty. Otherwise, we shouldn't need a reflow immediately after a
|
||||||
|
// reflow, except if we're waiting for a deferred paint.
|
||||||
assert!(!self.Document().needs_reflow() ||
|
assert!(!self.Document().needs_reflow() ||
|
||||||
|
(!for_display && self.Document().needs_paint()) ||
|
||||||
self.window_size.get().is_none() ||
|
self.window_size.get().is_none() ||
|
||||||
self.suppress_reflow.get());
|
self.suppress_reflow.get());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -405,6 +405,14 @@ impl<'ld> TDocument for ServoLayoutDocument<'ld> {
|
||||||
let elements = unsafe { self.document.drain_modified_elements() };
|
let elements = unsafe { self.document.drain_modified_elements() };
|
||||||
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
|
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn needs_paint_from_layout(&self) {
|
||||||
|
unsafe { self.document.needs_paint_from_layout(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn will_paint(&self) {
|
||||||
|
unsafe { self.document.will_paint(); }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'ld> ServoLayoutDocument<'ld> {
|
impl<'ld> ServoLayoutDocument<'ld> {
|
||||||
|
|
|
@ -179,6 +179,9 @@ pub trait TDocument : Sized + Copy + Clone {
|
||||||
|
|
||||||
fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement,
|
fn drain_modified_elements(&self) -> Vec<(Self::ConcreteElement,
|
||||||
<Self::ConcreteElement as ElementExt>::Snapshot)>;
|
<Self::ConcreteElement as ElementExt>::Snapshot)>;
|
||||||
|
|
||||||
|
fn needs_paint_from_layout(&self);
|
||||||
|
fn will_paint(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait PresentationalHintsSynthetizer {
|
pub trait PresentationalHintsSynthetizer {
|
||||||
|
|
|
@ -414,6 +414,8 @@ impl<'ld> TDocument for GeckoDocument<'ld> {
|
||||||
let elements = unsafe { self.document.drain_modified_elements() };
|
let elements = unsafe { self.document.drain_modified_elements() };
|
||||||
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()*/
|
elements.into_iter().map(|(el, snapshot)| (ServoLayoutElement::from_layout_js(el), snapshot)).collect()*/
|
||||||
}
|
}
|
||||||
|
fn will_paint(&self) { unimplemented!() }
|
||||||
|
fn needs_paint_from_layout(&self) { unimplemented!() }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue