mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
script: Ensure we don't ignore reflows for queries that bail out due to the viewport size not yet present.
This fixes #11223. Note that this didn't happen in the root pipeline because we explicitly set the window size in that case.
This commit is contained in:
parent
cba47174e0
commit
f08971ec88
1 changed files with 99 additions and 46 deletions
|
@ -1019,26 +1019,33 @@ impl Window {
|
||||||
/// and no reflow is performed. If reflow is suppressed, no reflow will be
|
/// and no reflow is performed. If reflow is suppressed, no reflow will be
|
||||||
/// performed for ForDisplay goals.
|
/// performed for ForDisplay goals.
|
||||||
///
|
///
|
||||||
/// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout.
|
/// TODO(pcwalton): Only wait for style recalc, since we have
|
||||||
pub fn force_reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
|
/// off-main-thread layout.
|
||||||
|
///
|
||||||
|
/// Returns true if layout actually happened, false otherwise.
|
||||||
|
pub fn force_reflow(&self,
|
||||||
|
goal: ReflowGoal,
|
||||||
|
query_type: ReflowQueryType,
|
||||||
|
reason: ReflowReason) -> bool {
|
||||||
// Check if we need to unsuppress reflow. Note that this needs to be
|
// Check if we need to unsuppress reflow. Note that this needs to be
|
||||||
// *before* any early bailouts, or reflow might never be unsuppresed!
|
// *before* any early bailouts, or reflow might never be unsuppresed!
|
||||||
match reason {
|
match reason {
|
||||||
ReflowReason::FirstLoad | ReflowReason::RefreshTick => self.suppress_reflow.set(false),
|
ReflowReason::FirstLoad |
|
||||||
|
ReflowReason::RefreshTick => self.suppress_reflow.set(false),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is no window size, we have nothing to do.
|
// If there is no window size, we have nothing to do.
|
||||||
let window_size = match self.window_size.get() {
|
let window_size = match self.window_size.get() {
|
||||||
Some(window_size) => window_size,
|
Some(window_size) => window_size,
|
||||||
None => return,
|
None => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
let for_display = query_type == ReflowQueryType::NoQuery;
|
let for_display = query_type == ReflowQueryType::NoQuery;
|
||||||
if for_display && self.suppress_reflow.get() {
|
if for_display && self.suppress_reflow.get() {
|
||||||
debug!("Suppressing reflow pipeline {} for goal {:?} reason {:?} before FirstLoad or RefreshTick",
|
debug!("Suppressing reflow pipeline {} for goal {:?} reason {:?} before FirstLoad or RefreshTick",
|
||||||
self.id, goal, reason);
|
self.id, goal, reason);
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("script: performing reflow for goal {:?} reason {:?}", goal, reason);
|
debug!("script: performing reflow for goal {:?} reason {:?}", goal, reason);
|
||||||
|
@ -1100,18 +1107,32 @@ impl Window {
|
||||||
if let Some(marker) = marker {
|
if let Some(marker) = marker {
|
||||||
self.emit_timeline_marker(marker.end());
|
self.emit_timeline_marker(marker.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reflows the page if it's possible to do so and the page is dirty. This method will wait
|
/// Reflows the page if it's possible to do so and the page is dirty. This
|
||||||
/// for the layout thread to complete (but see the `TODO` below). If there is no window size
|
/// method will wait for the layout thread to complete (but see the `TODO`
|
||||||
/// yet, the page is presumed invisible and no reflow is performed.
|
/// below). If there is no window size 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
|
||||||
pub fn reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
|
/// off-main-thread layout.
|
||||||
|
///
|
||||||
|
/// Returns true if layout actually happened, false otherwise.
|
||||||
|
/// This return value is useful for script queries, that wait for a lock
|
||||||
|
/// that layout might hold if the first layout hasn't happened yet (which
|
||||||
|
/// may happen in the only case a query reflow may bail out, that is, if the
|
||||||
|
/// viewport size is not present). See #11223 for an example of that.
|
||||||
|
pub fn reflow(&self,
|
||||||
|
goal: ReflowGoal,
|
||||||
|
query_type: ReflowQueryType,
|
||||||
|
reason: ReflowReason) -> bool {
|
||||||
let for_display = query_type == ReflowQueryType::NoQuery;
|
let for_display = query_type == ReflowQueryType::NoQuery;
|
||||||
|
|
||||||
|
let mut issued_reflow = false;
|
||||||
if !for_display || self.Document().needs_reflow() {
|
if !for_display || self.Document().needs_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 dirty.
|
||||||
// Otherwise, we shouldn't need a reflow immediately after a reflow.
|
// Otherwise, we shouldn't need a reflow immediately after a reflow.
|
||||||
|
@ -1147,6 +1168,8 @@ impl Window {
|
||||||
self.constellation_chan().send(event).unwrap();
|
self.constellation_chan().send(event).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issued_reflow
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn layout(&self) -> &LayoutRPC {
|
pub fn layout(&self) -> &LayoutRPC {
|
||||||
|
@ -1154,54 +1177,73 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
|
pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::ContentBoxQuery(content_box_request),
|
ReflowQueryType::ContentBoxQuery(content_box_request),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return Rect::zero();
|
||||||
|
}
|
||||||
let ContentBoxResponse(rect) = self.layout_rpc.content_box();
|
let ContentBoxResponse(rect) = self.layout_rpc.content_box();
|
||||||
rect
|
rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
|
pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::ContentBoxesQuery(content_boxes_request),
|
ReflowQueryType::ContentBoxesQuery(content_boxes_request),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return vec![];
|
||||||
|
}
|
||||||
let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
|
let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
|
||||||
rects
|
rects
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn client_rect_query(&self, node_geometry_request: TrustedNodeAddress) -> Rect<i32> {
|
pub fn client_rect_query(&self, node_geometry_request: TrustedNodeAddress) -> Rect<i32> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::NodeGeometryQuery(node_geometry_request),
|
ReflowQueryType::NodeGeometryQuery(node_geometry_request),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return Rect::zero();
|
||||||
|
}
|
||||||
self.layout_rpc.node_geometry().client_rect
|
self.layout_rpc.node_geometry().client_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_test_query(&self, hit_test_request: Point2D<f32>, update_cursor: bool)
|
pub fn hit_test_query(&self, hit_test_request: Point2D<f32>, update_cursor: bool)
|
||||||
-> Option<UntrustedNodeAddress> {
|
-> Option<UntrustedNodeAddress> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::HitTestQuery(hit_test_request, update_cursor),
|
ReflowQueryType::HitTestQuery(hit_test_request, update_cursor),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return None
|
||||||
|
}
|
||||||
|
|
||||||
self.layout_rpc.hit_test().node_address
|
self.layout_rpc.hit_test().node_address
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_area_query(&self, node: TrustedNodeAddress) -> Rect<i32> {
|
pub fn scroll_area_query(&self, node: TrustedNodeAddress) -> Rect<i32> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::NodeScrollGeometryQuery(node),
|
ReflowQueryType::NodeScrollGeometryQuery(node),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return Rect::zero();
|
||||||
|
}
|
||||||
self.layout_rpc.node_scroll_area().client_rect
|
self.layout_rpc.node_scroll_area().client_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn overflow_query(&self, node: TrustedNodeAddress) -> Point2D<overflow_x::computed_value::T> {
|
pub fn overflow_query(&self,
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
node: TrustedNodeAddress) -> Point2D<overflow_x::computed_value::T> {
|
||||||
ReflowQueryType::NodeOverflowQuery(node),
|
// NB: This is only called if the document is fully active, and the only
|
||||||
ReflowReason::Query);
|
// reason to bail out from a query is if there's no viewport, so this
|
||||||
|
// *must* issue a reflow.
|
||||||
|
assert!(self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
|
ReflowQueryType::NodeOverflowQuery(node),
|
||||||
|
ReflowReason::Query));
|
||||||
|
|
||||||
self.layout_rpc.node_overflow().0.unwrap()
|
self.layout_rpc.node_overflow().0.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn scroll_offset_query(&self, node: TrustedNodeAddress) -> Point2D<f32> {
|
pub fn scroll_offset_query(&self, node: TrustedNodeAddress) -> Point2D<f32> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::NodeLayerIdQuery(node),
|
ReflowQueryType::NodeLayerIdQuery(node),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return Point2D::zero();
|
||||||
|
}
|
||||||
|
|
||||||
let layer_id = self.layout_rpc.node_layer_id().layer_id;
|
let layer_id = self.layout_rpc.node_layer_id().layer_id;
|
||||||
let pipeline_id = self.id;
|
let pipeline_id = self.id;
|
||||||
|
|
||||||
|
@ -1213,9 +1255,11 @@ impl Window {
|
||||||
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
|
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
|
||||||
pub fn scroll_node(&self, node: TrustedNodeAddress,
|
pub fn scroll_node(&self, node: TrustedNodeAddress,
|
||||||
x_: f64, y_: f64, behavior: ScrollBehavior) {
|
x_: f64, y_: f64, behavior: ScrollBehavior) {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::NodeLayerIdQuery(node),
|
ReflowQueryType::NodeLayerIdQuery(node),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let layer_id = self.layout_rpc.node_layer_id().layer_id;
|
let layer_id = self.layout_rpc.node_layer_id().layer_id;
|
||||||
|
|
||||||
|
@ -1228,17 +1272,22 @@ impl Window {
|
||||||
element: TrustedNodeAddress,
|
element: TrustedNodeAddress,
|
||||||
pseudo: Option<PseudoElement>,
|
pseudo: Option<PseudoElement>,
|
||||||
property: &Atom) -> Option<DOMString> {
|
property: &Atom) -> Option<DOMString> {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()),
|
ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
let ResolvedStyleResponse(resolved) = self.layout_rpc.resolved_style();
|
let ResolvedStyleResponse(resolved) = self.layout_rpc.resolved_style();
|
||||||
resolved.map(DOMString::from)
|
resolved.map(DOMString::from)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn offset_parent_query(&self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) {
|
pub fn offset_parent_query(&self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::OffsetParentQuery(node),
|
ReflowQueryType::OffsetParentQuery(node),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return (None, Rect::zero());
|
||||||
|
}
|
||||||
|
|
||||||
let response = self.layout_rpc.offset_parent();
|
let response = self.layout_rpc.offset_parent();
|
||||||
let js_runtime = self.js_runtime.borrow();
|
let js_runtime = self.js_runtime.borrow();
|
||||||
let js_runtime = js_runtime.as_ref().unwrap();
|
let js_runtime = js_runtime.as_ref().unwrap();
|
||||||
|
@ -1250,9 +1299,11 @@ impl Window {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn margin_style_query(&self, node: TrustedNodeAddress) -> MarginStyleResponse {
|
pub fn margin_style_query(&self, node: TrustedNodeAddress) -> MarginStyleResponse {
|
||||||
self.reflow(ReflowGoal::ForScriptQuery,
|
if !self.reflow(ReflowGoal::ForScriptQuery,
|
||||||
ReflowQueryType::MarginStyleQuery(node),
|
ReflowQueryType::MarginStyleQuery(node),
|
||||||
ReflowReason::Query);
|
ReflowReason::Query) {
|
||||||
|
return MarginStyleResponse::empty();
|
||||||
|
}
|
||||||
self.layout_rpc.margin_style()
|
self.layout_rpc.margin_style()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1276,7 +1327,9 @@ impl Window {
|
||||||
|
|
||||||
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
|
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
|
||||||
self.timers.fire_timer(timer_id, self);
|
self.timers.fire_timer(timer_id, self);
|
||||||
self.reflow(ReflowGoal::ForDisplay, ReflowQueryType::NoQuery, ReflowReason::Timer);
|
self.reflow(ReflowGoal::ForDisplay,
|
||||||
|
ReflowQueryType::NoQuery,
|
||||||
|
ReflowReason::Timer);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_fragment_name(&self, fragment: Option<String>) {
|
pub fn set_fragment_name(&self, fragment: Option<String>) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue