mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
Auto merge of #13401 - notriddle:master, r=pcwalton
Implement sequential fallback to float speculation This shouldn't impact any pages that are already rendering correctly, but it is a very naive implementation of this pass. --- - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes fix #13284 and fix #13223 - [X] There are tests for these changes <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/13401) <!-- Reviewable:end -->
This commit is contained in:
commit
4ebecc915a
11 changed files with 193 additions and 35 deletions
|
@ -52,6 +52,7 @@ use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none};
|
|||
use rustc_serialize::{Encodable, Encoder};
|
||||
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
|
||||
use script_layout_interface::restyle_damage::REPOSITION;
|
||||
use sequential;
|
||||
use std::cmp::{max, min};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
@ -62,6 +63,7 @@ use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMod
|
|||
use style::properties::ServoComputedValues;
|
||||
use style::values::computed::{LengthOrNone, LengthOrPercentageOrNone};
|
||||
use style::values::computed::{LengthOrPercentage, LengthOrPercentageOrAuto};
|
||||
use util::clamp;
|
||||
use util::geometry::max_rect;
|
||||
|
||||
/// The number of screens of data we're allowed to generate display lists for in each direction.
|
||||
|
@ -781,6 +783,7 @@ impl BlockFlow {
|
|||
self.base.debug_id());
|
||||
|
||||
let mut break_at = None;
|
||||
let content_box = self.fragment.content_box();
|
||||
if self.base.restyle_damage.contains(REFLOW) {
|
||||
self.determine_if_layer_needed();
|
||||
|
||||
|
@ -830,7 +833,8 @@ impl BlockFlow {
|
|||
kid.place_float_if_applicable();
|
||||
if !flow::base(kid).flags.is_float() {
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
thread_id);
|
||||
thread_id,
|
||||
content_box);
|
||||
}
|
||||
|
||||
// Skip the collapsing and float processing for absolute flow kids and continue
|
||||
|
@ -881,7 +885,8 @@ impl BlockFlow {
|
|||
// Lay the child out if this was an in-order traversal.
|
||||
let need_to_process_child_floats =
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
thread_id);
|
||||
thread_id,
|
||||
content_box);
|
||||
|
||||
if !had_children_with_clearance &&
|
||||
floats.is_present() &&
|
||||
|
@ -1062,7 +1067,9 @@ impl BlockFlow {
|
|||
// necessary.
|
||||
let thread_id = self.base.thread_id;
|
||||
for kid in self.base.child_iter_mut() {
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id);
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
thread_id,
|
||||
content_box);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1457,32 +1464,102 @@ impl BlockFlow {
|
|||
/// on the floats we could see at the time of inline-size assignment. The job of this function,
|
||||
/// therefore, is not only to assign the final size but also to perform the layout again for
|
||||
/// this block formatting context if our speculation was wrong.
|
||||
///
|
||||
/// FIXME(pcwalton): This code is not incremental-reflow-safe (i.e. not idempotent).
|
||||
fn assign_inline_position_for_formatting_context(&mut self) {
|
||||
fn assign_inline_position_for_formatting_context<'a>(&mut self,
|
||||
layout_context: &'a LayoutContext<'a>,
|
||||
content_box: LogicalRect<Au>) {
|
||||
debug_assert!(self.formatting_context_type() != FormattingContextType::None);
|
||||
|
||||
if !self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
|
||||
return
|
||||
}
|
||||
|
||||
let info = PlacementInfo {
|
||||
size: self.fragment.border_box.size.convert(self.fragment.style.writing_mode,
|
||||
self.base.floats.writing_mode),
|
||||
ceiling: self.base.position.start.b,
|
||||
max_inline_size: MAX_AU,
|
||||
kind: FloatKind::Left,
|
||||
};
|
||||
|
||||
// Offset our position by whatever displacement is needed to not impact the floats.
|
||||
let rect = self.base.floats.place_between_floats(&info);
|
||||
self.base.position.start.i = self.base.position.start.i + rect.start.i;
|
||||
|
||||
// TODO(pcwalton): If the inline-size of this flow is different from the size we estimated
|
||||
// earlier, lay it out again.
|
||||
|
||||
// We do this first to avoid recomputing our inline size when we propagate it.
|
||||
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
||||
self.fragment.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
||||
|
||||
// The code below would completely wreck the layout if run on a flex item, however:
|
||||
// * Flex items are always the children of flex containers.
|
||||
// * Flex containers only contain flex items.
|
||||
// * Floats cannot intrude into flex containers.
|
||||
// * Floats cannot escape flex items.
|
||||
// * Flex items cannot also be floats.
|
||||
// Therefore, a flex item cannot be impacted by a float.
|
||||
// See also: https://www.w3.org/TR/css-flexbox-1/#flex-containers
|
||||
if !self.base.might_have_floats_in() {
|
||||
return
|
||||
}
|
||||
|
||||
// If you remove the might_have_floats_in conditional, this will go off.
|
||||
debug_assert!(!self.is_flex());
|
||||
|
||||
// Compute the available space for us, based on the actual floats.
|
||||
let rect = self.base.floats.available_rect(
|
||||
self.base.position.start.b,
|
||||
self.fragment.border_box.size.block,
|
||||
content_box.size.inline
|
||||
);
|
||||
let available_inline_size = if let Some(rect) = rect {
|
||||
// Offset our position by whatever displacement is needed to not impact the floats.
|
||||
// Also, account for margins sliding behind floats.
|
||||
let inline_offset = if self.fragment.margin.inline_start < rect.start.i {
|
||||
// Do not do anything for negative margins; those are handled separately.
|
||||
rect.start.i - max(Au(0), self.fragment.margin.inline_start)
|
||||
} else {
|
||||
Au(0)
|
||||
};
|
||||
self.base.position.start.i = content_box.start.i + inline_offset;
|
||||
// Handle the end margin sliding behind the float.
|
||||
let end = content_box.size.inline - rect.start.i - rect.size.inline;
|
||||
let inline_end_offset = if self.fragment.margin.inline_end < end {
|
||||
end - max(Au(0), self.fragment.margin.inline_end)
|
||||
} else {
|
||||
Au(0)
|
||||
};
|
||||
content_box.size.inline - inline_offset - inline_end_offset
|
||||
} else {
|
||||
content_box.size.inline
|
||||
} - self.fragment.margin.inline_start_end();
|
||||
let max_inline_size = specified_or_none(
|
||||
self.fragment.style().max_inline_size(),
|
||||
self.base.block_container_inline_size
|
||||
).unwrap_or(MAX_AU);
|
||||
let min_inline_size = specified(
|
||||
self.fragment.style().min_inline_size(),
|
||||
self.base.block_container_inline_size
|
||||
);
|
||||
let specified_inline_size = self.fragment.style().content_inline_size();
|
||||
let container_size = self.base.block_container_inline_size;
|
||||
let inline_size =
|
||||
if let MaybeAuto::Specified(size) = MaybeAuto::from_style(specified_inline_size,
|
||||
container_size) {
|
||||
match self.fragment.style().get_position().box_sizing {
|
||||
box_sizing::T::border_box => size,
|
||||
box_sizing::T::content_box =>
|
||||
size + self.fragment.border_padding.inline_start_end(),
|
||||
}
|
||||
} else {
|
||||
clamp(min_inline_size, available_inline_size, max_inline_size)
|
||||
};
|
||||
self.base.position.size.inline = inline_size + self.fragment.margin.inline_start_end();
|
||||
|
||||
// If float speculation failed, fixup our layout, and re-layout all the children.
|
||||
if self.fragment.margin_box_inline_size() != self.base.position.size.inline {
|
||||
debug!("assign_inline_position_for_formatting_context: float speculation failed");
|
||||
// Fix-up our own layout.
|
||||
// We can't just traverse_flow_tree_preorder ourself, because that would re-run
|
||||
// float speculation, instead of acting on the actual results.
|
||||
self.fragment.border_box.size.inline = inline_size;
|
||||
// Assign final-final inline sizes on all our children.
|
||||
self.assign_inline_sizes(&layout_context.shared.style_context);
|
||||
// Re-run layout on our children.
|
||||
for child in flow::mut_base(self).children.iter_mut() {
|
||||
sequential::traverse_flow_tree_preorder(child, layout_context.shared);
|
||||
}
|
||||
// Assign our final-final block size.
|
||||
self.assign_block_size(layout_context);
|
||||
}
|
||||
|
||||
debug_assert_eq!(self.fragment.margin_box_inline_size(), self.base.position.size.inline);
|
||||
}
|
||||
|
||||
fn is_inline_block(&self) -> bool {
|
||||
|
@ -1792,7 +1869,8 @@ impl Flow for BlockFlow {
|
|||
|
||||
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
||||
layout_context: &'a LayoutContext<'a>,
|
||||
parent_thread_id: u8)
|
||||
parent_thread_id: u8,
|
||||
content_box: LogicalRect<Au>)
|
||||
-> bool {
|
||||
if self.base.flags.is_float() {
|
||||
return false
|
||||
|
@ -1800,7 +1878,7 @@ impl Flow for BlockFlow {
|
|||
|
||||
let is_formatting_context = self.formatting_context_type() != FormattingContextType::None;
|
||||
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) && is_formatting_context {
|
||||
self.assign_inline_position_for_formatting_context();
|
||||
self.assign_inline_position_for_formatting_context(layout_context, content_box);
|
||||
}
|
||||
|
||||
if (self as &Flow).floats_might_flow_through() {
|
||||
|
|
|
@ -238,7 +238,8 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
|
|||
/// it as laid out by its parent.
|
||||
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
||||
layout_context: &'a LayoutContext<'a>,
|
||||
parent_thread_id: u8)
|
||||
parent_thread_id: u8,
|
||||
_content_box: LogicalRect<Au>)
|
||||
-> bool {
|
||||
let might_have_floats_in_or_out = base(self).might_have_floats_in() ||
|
||||
base(self).might_have_floats_out();
|
||||
|
|
|
@ -1482,7 +1482,10 @@ impl Flow for InlineFlow {
|
|||
flow::base(kid).flags.is_float() {
|
||||
continue
|
||||
}
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id);
|
||||
let content_box = flow::base(kid).position;
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
thread_id,
|
||||
content_box);
|
||||
}
|
||||
|
||||
if self.contains_positioned_fragments() {
|
||||
|
|
|
@ -111,11 +111,15 @@ impl TableRowFlow {
|
|||
// all cells).
|
||||
let mut max_block_size = Au(0);
|
||||
let thread_id = self.block_flow.base.thread_id;
|
||||
let content_box = self.block_flow.base.position
|
||||
- self.block_flow.fragment.border_padding
|
||||
- self.block_flow.fragment.margin;
|
||||
for kid in self.block_flow.base.child_iter_mut() {
|
||||
kid.place_float_if_applicable();
|
||||
if !flow::base(kid).flags.is_float() {
|
||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
thread_id);
|
||||
thread_id,
|
||||
content_box);
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ use std::ops::Add;
|
|||
use std::sync::Arc;
|
||||
use style::computed_values::{border_collapse, table_layout};
|
||||
use style::context::SharedStyleContext;
|
||||
use style::logical_geometry::LogicalSize;
|
||||
use style::logical_geometry::{LogicalRect, LogicalSize};
|
||||
use style::properties::ServoComputedValues;
|
||||
use style::values::CSSFloat;
|
||||
use style::values::computed::LengthOrPercentageOrAuto;
|
||||
|
@ -422,10 +422,12 @@ impl Flow for TableWrapperFlow {
|
|||
|
||||
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
||||
layout_context: &'a LayoutContext<'a>,
|
||||
parent_thread_id: u8)
|
||||
parent_thread_id: u8,
|
||||
content_box: LogicalRect<Au>)
|
||||
-> bool {
|
||||
self.block_flow.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||
parent_thread_id)
|
||||
parent_thread_id,
|
||||
content_box)
|
||||
}
|
||||
|
||||
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
|
||||
|
|
|
@ -44,3 +44,13 @@ pub fn servo_version() -> String {
|
|||
None => format!("Servo {}", cargo_version),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clamp<T: Ord>(lo: T, mid: T, hi: T) -> T {
|
||||
if mid < lo {
|
||||
lo
|
||||
} else if mid > hi {
|
||||
hi
|
||||
} else {
|
||||
mid
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
[block-formatting-contexts-015.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1,3 +0,0 @@
|
|||
[floats-bfc-002.htm]
|
||||
type: reftest
|
||||
expected: FAIL
|
|
@ -1592,6 +1592,18 @@
|
|||
"url": "/_mozilla/css/float_clearance_intrinsic_width_a.html"
|
||||
}
|
||||
],
|
||||
"css/float_cleared_with_just_height.html": [
|
||||
{
|
||||
"path": "css/float_cleared_with_just_height.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/float_cleared_with_just_height_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/float_cleared_with_just_height.html"
|
||||
}
|
||||
],
|
||||
"css/float_intrinsic_height.html": [
|
||||
{
|
||||
"path": "css/float_intrinsic_height.html",
|
||||
|
@ -15242,6 +15254,18 @@
|
|||
"url": "/_mozilla/css/float_clearance_intrinsic_width_a.html"
|
||||
}
|
||||
],
|
||||
"css/float_cleared_with_just_height.html": [
|
||||
{
|
||||
"path": "css/float_cleared_with_just_height.html",
|
||||
"references": [
|
||||
[
|
||||
"/_mozilla/css/float_cleared_with_just_height_ref.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
"url": "/_mozilla/css/float_cleared_with_just_height.html"
|
||||
}
|
||||
],
|
||||
"css/float_intrinsic_height.html": [
|
||||
{
|
||||
"path": "css/float_intrinsic_height.html",
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>floats can be cleared with just height</title>
|
||||
<link rel="match" href="float_cleared_with_just_height_ref.html">
|
||||
<style>
|
||||
.a {
|
||||
height: 20px;
|
||||
}
|
||||
.b {
|
||||
height: 10px;
|
||||
width: 50px;
|
||||
float: right;
|
||||
}
|
||||
.c {
|
||||
border: solid 1px #000;
|
||||
background: red;
|
||||
height: 10px;
|
||||
overflow: auto
|
||||
}
|
||||
</style>
|
||||
<div class=b></div>
|
||||
<div class=a></div>
|
||||
<div class=b></div>
|
||||
<div class=a></div>
|
||||
<div class=c></div>
|
|
@ -0,0 +1,17 @@
|
|||
<!doctype html>
|
||||
<meta charset="utf-8">
|
||||
<title>REFERENCE: floats can be cleared with just height</title>
|
||||
<style>
|
||||
.a {
|
||||
height: 20px;
|
||||
}
|
||||
.c {
|
||||
border: solid 1px #000;
|
||||
background: red;
|
||||
height: 10px;
|
||||
overflow: auto
|
||||
}
|
||||
</style>
|
||||
<div class=a></div>
|
||||
<div class=a></div>
|
||||
<div class=c></div>
|
Loading…
Add table
Add a link
Reference in a new issue