mirror of
https://github.com/servo/servo.git
synced 2025-08-03 12:40:06 +01:00
Implement sequential fallback to float speculation
Fixes #13284 Fixes #13223
This commit is contained in:
parent
ef1b594f48
commit
fe2018682b
10 changed files with 184 additions and 35 deletions
|
@ -52,6 +52,7 @@ use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none};
|
||||||
use rustc_serialize::{Encodable, Encoder};
|
use rustc_serialize::{Encodable, Encoder};
|
||||||
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
|
use script_layout_interface::restyle_damage::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW};
|
||||||
use script_layout_interface::restyle_damage::REPOSITION;
|
use script_layout_interface::restyle_damage::REPOSITION;
|
||||||
|
use sequential;
|
||||||
use std::cmp::{max, min};
|
use std::cmp::{max, min};
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -781,6 +782,7 @@ impl BlockFlow {
|
||||||
self.base.debug_id());
|
self.base.debug_id());
|
||||||
|
|
||||||
let mut break_at = None;
|
let mut break_at = None;
|
||||||
|
let content_box = self.fragment.content_box();
|
||||||
if self.base.restyle_damage.contains(REFLOW) {
|
if self.base.restyle_damage.contains(REFLOW) {
|
||||||
self.determine_if_layer_needed();
|
self.determine_if_layer_needed();
|
||||||
|
|
||||||
|
@ -830,7 +832,8 @@ impl BlockFlow {
|
||||||
kid.place_float_if_applicable();
|
kid.place_float_if_applicable();
|
||||||
if !flow::base(kid).flags.is_float() {
|
if !flow::base(kid).flags.is_float() {
|
||||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
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
|
// Skip the collapsing and float processing for absolute flow kids and continue
|
||||||
|
@ -881,7 +884,8 @@ impl BlockFlow {
|
||||||
// Lay the child out if this was an in-order traversal.
|
// Lay the child out if this was an in-order traversal.
|
||||||
let need_to_process_child_floats =
|
let need_to_process_child_floats =
|
||||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||||
thread_id);
|
thread_id,
|
||||||
|
content_box);
|
||||||
|
|
||||||
if !had_children_with_clearance &&
|
if !had_children_with_clearance &&
|
||||||
floats.is_present() &&
|
floats.is_present() &&
|
||||||
|
@ -1062,7 +1066,9 @@ impl BlockFlow {
|
||||||
// necessary.
|
// necessary.
|
||||||
let thread_id = self.base.thread_id;
|
let thread_id = self.base.thread_id;
|
||||||
for kid in self.base.child_iter_mut() {
|
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 +1463,104 @@ impl BlockFlow {
|
||||||
/// on the floats we could see at the time of inline-size assignment. The job of this function,
|
/// 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
|
/// 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.
|
/// this block formatting context if our speculation was wrong.
|
||||||
///
|
fn assign_inline_position_for_formatting_context<'a>(&mut self,
|
||||||
/// FIXME(pcwalton): This code is not incremental-reflow-safe (i.e. not idempotent).
|
layout_context: &'a LayoutContext<'a>,
|
||||||
fn assign_inline_position_for_formatting_context(&mut self) {
|
content_box: LogicalRect<Au>) {
|
||||||
debug_assert!(self.formatting_context_type() != FormattingContextType::None);
|
debug_assert!(self.formatting_context_type() != FormattingContextType::None);
|
||||||
|
|
||||||
if !self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
|
if !self.base.restyle_damage.intersects(REFLOW_OUT_OF_FLOW | REFLOW) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
let info = PlacementInfo {
|
// We do this first to avoid recomputing our inline size when we propagate it.
|
||||||
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.
|
|
||||||
|
|
||||||
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
self.base.restyle_damage.remove(REFLOW_OUT_OF_FLOW | REFLOW);
|
||||||
self.fragment.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
|
||||||
|
// This line is not just an optimization. It's also needed for correctness.
|
||||||
|
if !self.base.might_have_floats_in() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 if available_inline_size > max_inline_size {
|
||||||
|
max_inline_size
|
||||||
|
} else if available_inline_size < min_inline_size {
|
||||||
|
min_inline_size
|
||||||
|
} else {
|
||||||
|
available_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 {
|
fn is_inline_block(&self) -> bool {
|
||||||
|
@ -1792,7 +1870,8 @@ impl Flow for BlockFlow {
|
||||||
|
|
||||||
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
||||||
layout_context: &'a LayoutContext<'a>,
|
layout_context: &'a LayoutContext<'a>,
|
||||||
parent_thread_id: u8)
|
parent_thread_id: u8,
|
||||||
|
content_box: LogicalRect<Au>)
|
||||||
-> bool {
|
-> bool {
|
||||||
if self.base.flags.is_float() {
|
if self.base.flags.is_float() {
|
||||||
return false
|
return false
|
||||||
|
@ -1800,7 +1879,7 @@ impl Flow for BlockFlow {
|
||||||
|
|
||||||
let is_formatting_context = self.formatting_context_type() != FormattingContextType::None;
|
let is_formatting_context = self.formatting_context_type() != FormattingContextType::None;
|
||||||
if !self.base.flags.contains(IS_ABSOLUTELY_POSITIONED) && is_formatting_context {
|
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() {
|
if (self as &Flow).floats_might_flow_through() {
|
||||||
|
|
|
@ -241,7 +241,8 @@ pub trait Flow: fmt::Debug + Sync + Send + 'static {
|
||||||
/// it as laid out by its parent.
|
/// it as laid out by its parent.
|
||||||
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
||||||
layout_context: &'a LayoutContext<'a>,
|
layout_context: &'a LayoutContext<'a>,
|
||||||
parent_thread_id: u8)
|
parent_thread_id: u8,
|
||||||
|
_content_box: LogicalRect<Au>)
|
||||||
-> bool {
|
-> bool {
|
||||||
let might_have_floats_in_or_out = base(self).might_have_floats_in() ||
|
let might_have_floats_in_or_out = base(self).might_have_floats_in() ||
|
||||||
base(self).might_have_floats_out();
|
base(self).might_have_floats_out();
|
||||||
|
|
|
@ -1481,7 +1481,10 @@ impl Flow for InlineFlow {
|
||||||
flow::base(kid).flags.is_float() {
|
flow::base(kid).flags.is_float() {
|
||||||
continue
|
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() {
|
if self.contains_positioned_fragments() {
|
||||||
|
|
|
@ -112,11 +112,15 @@ impl TableRowFlow {
|
||||||
// all cells).
|
// all cells).
|
||||||
let mut max_block_size = Au(0);
|
let mut max_block_size = Au(0);
|
||||||
let thread_id = self.block_flow.base.thread_id;
|
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() {
|
for kid in self.block_flow.base.child_iter_mut() {
|
||||||
kid.place_float_if_applicable();
|
kid.place_float_if_applicable();
|
||||||
if !flow::base(kid).flags.is_float() {
|
if !flow::base(kid).flags.is_float() {
|
||||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
kid.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
||||||
thread_id);
|
thread_id,
|
||||||
|
content_box);
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,7 +32,7 @@ use std::ops::Add;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use style::computed_values::{border_collapse, table_layout};
|
use style::computed_values::{border_collapse, table_layout};
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
use style::logical_geometry::LogicalSize;
|
use style::logical_geometry::{LogicalRect, LogicalSize};
|
||||||
use style::properties::ServoComputedValues;
|
use style::properties::ServoComputedValues;
|
||||||
use style::values::CSSFloat;
|
use style::values::CSSFloat;
|
||||||
use style::values::computed::LengthOrPercentageOrAuto;
|
use style::values::computed::LengthOrPercentageOrAuto;
|
||||||
|
@ -423,10 +423,12 @@ impl Flow for TableWrapperFlow {
|
||||||
|
|
||||||
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
fn assign_block_size_for_inorder_child_if_necessary<'a>(&mut self,
|
||||||
layout_context: &'a LayoutContext<'a>,
|
layout_context: &'a LayoutContext<'a>,
|
||||||
parent_thread_id: u8)
|
parent_thread_id: u8,
|
||||||
|
content_box: LogicalRect<Au>)
|
||||||
-> bool {
|
-> bool {
|
||||||
self.block_flow.assign_block_size_for_inorder_child_if_necessary(layout_context,
|
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) {
|
fn update_late_computed_inline_position_if_necessary(&mut self, inline_position: Au) {
|
||||||
|
|
|
@ -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"
|
"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": [
|
"css/float_intrinsic_height.html": [
|
||||||
{
|
{
|
||||||
"path": "css/float_intrinsic_height.html",
|
"path": "css/float_intrinsic_height.html",
|
||||||
|
@ -15230,6 +15242,18 @@
|
||||||
"url": "/_mozilla/css/float_clearance_intrinsic_width_a.html"
|
"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": [
|
"css/float_intrinsic_height.html": [
|
||||||
{
|
{
|
||||||
"path": "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