servo: Merge #11545 - script: Ensure we don't ignore reflows for queries that bail out due to the viewport size not yet present (from emilio:reddit); r=pcwalton

<!-- Please describe your changes on the following line: -->

---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `__` with appropriate data: -->
- [x] `./mach build -d` does not report any errors
- [x] `./mach test-tidy` does not report any errors
- [x] These changes fix #11223

<!-- Either: -->
- [ ] There are tests for these changes.

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

This fixes #11223.

Note that this didn't happen in the root pipeline because we explicitly set the
window size in that case.

Source-Repo: https://github.com/servo/servo
Source-Revision: 4c8c57992b9addb8d45a1f1f5593f7490d975734
This commit is contained in:
Emilio Cobos Álvarez 2016-06-01 12:54:28 -05:00
Родитель 8e6f67b2b4
Коммит 177d019ac0
1 изменённых файлов: 99 добавлений и 46 удалений

Просмотреть файл

@ -1019,26 +1019,33 @@ impl Window {
/// and no reflow is performed. If reflow is suppressed, no reflow will be
/// performed for ForDisplay goals.
///
/// TODO(pcwalton): Only wait for style recalc, since we have off-main-thread layout.
pub fn force_reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
/// TODO(pcwalton): Only wait for style recalc, since we have
/// 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
// *before* any early bailouts, or reflow might never be unsuppresed!
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.
let window_size = match self.window_size.get() {
Some(window_size) => window_size,
None => return,
None => return false,
};
let for_display = query_type == ReflowQueryType::NoQuery;
if for_display && self.suppress_reflow.get() {
debug!("Suppressing reflow pipeline {} for goal {:?} reason {:?} before FirstLoad or RefreshTick",
self.id, goal, reason);
return;
return false;
}
debug!("script: performing reflow for goal {:?} reason {:?}", goal, reason);
@ -1100,18 +1107,32 @@ impl Window {
if let Some(marker) = marker {
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
/// for the layout thread to complete (but see the `TODO` below). If there is no window size
/// yet, the page is presumed invisible and no reflow is performed.
/// Reflows the page if it's possible to do so and the page is dirty. This
/// method will wait for the layout thread to complete (but see the `TODO`
/// 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.
pub fn reflow(&self, goal: ReflowGoal, query_type: ReflowQueryType, reason: ReflowReason) {
/// TODO(pcwalton): Only wait for style recalc, since we have
/// 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 mut issued_reflow = false;
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.
// Otherwise, we shouldn't need a reflow immediately after a reflow.
@ -1147,6 +1168,8 @@ impl Window {
self.constellation_chan().send(event).unwrap();
}
}
issued_reflow
}
pub fn layout(&self) -> &LayoutRPC {
@ -1154,54 +1177,73 @@ impl Window {
}
pub fn content_box_query(&self, content_box_request: TrustedNodeAddress) -> Rect<Au> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::ContentBoxQuery(content_box_request),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::ContentBoxQuery(content_box_request),
ReflowReason::Query) {
return Rect::zero();
}
let ContentBoxResponse(rect) = self.layout_rpc.content_box();
rect
}
pub fn content_boxes_query(&self, content_boxes_request: TrustedNodeAddress) -> Vec<Rect<Au>> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::ContentBoxesQuery(content_boxes_request),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::ContentBoxesQuery(content_boxes_request),
ReflowReason::Query) {
return vec![];
}
let ContentBoxesResponse(rects) = self.layout_rpc.content_boxes();
rects
}
pub fn client_rect_query(&self, node_geometry_request: TrustedNodeAddress) -> Rect<i32> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeGeometryQuery(node_geometry_request),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeGeometryQuery(node_geometry_request),
ReflowReason::Query) {
return Rect::zero();
}
self.layout_rpc.node_geometry().client_rect
}
pub fn hit_test_query(&self, hit_test_request: Point2D<f32>, update_cursor: bool)
-> Option<UntrustedNodeAddress> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::HitTestQuery(hit_test_request, update_cursor),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::HitTestQuery(hit_test_request, update_cursor),
ReflowReason::Query) {
return None
}
self.layout_rpc.hit_test().node_address
}
pub fn scroll_area_query(&self, node: TrustedNodeAddress) -> Rect<i32> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeScrollGeometryQuery(node),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeScrollGeometryQuery(node),
ReflowReason::Query) {
return Rect::zero();
}
self.layout_rpc.node_scroll_area().client_rect
}
pub fn overflow_query(&self, node: TrustedNodeAddress) -> Point2D<overflow_x::computed_value::T> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeOverflowQuery(node),
ReflowReason::Query);
pub fn overflow_query(&self,
node: TrustedNodeAddress) -> Point2D<overflow_x::computed_value::T> {
// NB: This is only called if the document is fully active, and the only
// 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()
}
pub fn scroll_offset_query(&self, node: TrustedNodeAddress) -> Point2D<f32> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeLayerIdQuery(node),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeLayerIdQuery(node),
ReflowReason::Query) {
return Point2D::zero();
}
let layer_id = self.layout_rpc.node_layer_id().layer_id;
let pipeline_id = self.id;
@ -1213,9 +1255,11 @@ impl Window {
// https://drafts.csswg.org/cssom-view/#dom-element-scroll
pub fn scroll_node(&self, node: TrustedNodeAddress,
x_: f64, y_: f64, behavior: ScrollBehavior) {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeLayerIdQuery(node),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::NodeLayerIdQuery(node),
ReflowReason::Query) {
return;
}
let layer_id = self.layout_rpc.node_layer_id().layer_id;
@ -1228,17 +1272,22 @@ impl Window {
element: TrustedNodeAddress,
pseudo: Option<PseudoElement>,
property: &Atom) -> Option<DOMString> {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::ResolvedStyleQuery(element, pseudo, property.clone()),
ReflowReason::Query) {
return None;
}
let ResolvedStyleResponse(resolved) = self.layout_rpc.resolved_style();
resolved.map(DOMString::from)
}
pub fn offset_parent_query(&self, node: TrustedNodeAddress) -> (Option<Root<Element>>, Rect<Au>) {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::OffsetParentQuery(node),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::OffsetParentQuery(node),
ReflowReason::Query) {
return (None, Rect::zero());
}
let response = self.layout_rpc.offset_parent();
let js_runtime = self.js_runtime.borrow();
let js_runtime = js_runtime.as_ref().unwrap();
@ -1250,9 +1299,11 @@ impl Window {
}
pub fn margin_style_query(&self, node: TrustedNodeAddress) -> MarginStyleResponse {
self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::MarginStyleQuery(node),
ReflowReason::Query);
if !self.reflow(ReflowGoal::ForScriptQuery,
ReflowQueryType::MarginStyleQuery(node),
ReflowReason::Query) {
return MarginStyleResponse::empty();
}
self.layout_rpc.margin_style()
}
@ -1276,7 +1327,9 @@ impl Window {
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {
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>) {