Propogate transformed clipping regions to nested flows.

Fixes #10559
This commit is contained in:
Michael Howell 2016-04-13 11:36:21 -07:00
parent 6c9efbf383
commit 3c2210c5fc
5 changed files with 108 additions and 47 deletions

View file

@ -48,13 +48,13 @@ use incremental::{BUBBLE_ISIZES, REFLOW, REFLOW_OUT_OF_FLOW, REPAINT};
use layout_debug; use layout_debug;
use layout_thread::DISPLAY_PORT_SIZE_FACTOR; use layout_thread::DISPLAY_PORT_SIZE_FACTOR;
use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none}; use model::{CollapsibleMargins, MaybeAuto, specified, specified_or_none};
use model::{IntrinsicISizes, MarginCollapseInfo}; use model::{self, IntrinsicISizes, MarginCollapseInfo};
use rustc_serialize::{Encodable, Encoder}; use rustc_serialize::{Encodable, Encoder};
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::fmt; use std::fmt;
use std::sync::Arc; use std::sync::Arc;
use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y}; use style::computed_values::{border_collapse, box_sizing, display, float, overflow_x, overflow_y};
use style::computed_values::{position, text_align, transform_style}; use style::computed_values::{position, text_align, transform, transform_style};
use style::context::StyleContext; use style::context::StyleContext;
use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode}; use style::logical_geometry::{LogicalPoint, LogicalRect, LogicalSize, WritingMode};
use style::properties::{ComputedValues, ServoComputedValues}; use style::properties::{ComputedValues, ServoComputedValues};
@ -1965,7 +1965,48 @@ impl Flow for BlockFlow {
flow::mut_base(kid).late_absolute_position_info = flow::mut_base(kid).late_absolute_position_info =
late_absolute_position_info_for_children; late_absolute_position_info_for_children;
flow::mut_base(kid).clip = clip.clone(); let clip = if kid.is_block_like() {
let mut clip = clip.clone();
let kid = kid.as_block();
// TODO(notriddle): To properly support transformations, we either need
// non-rectangular clipping regions in display lists, or clipping
// regions in terms of the parent coordinate system instead of the
// child coordinate system.
//
// This is a workaround for a common idiom of transform: translate().
if let Some(ref operations) = kid.fragment.style().get_effects().transform.0 {
for operation in operations {
match *operation {
transform::ComputedOperation::Translate(tx, ty, _) => {
// N.B. When the clipping value comes from us, it
// shouldn't be transformed.
let tx = if let overflow_x::T::hidden = kid.fragment.style().get_box()
.overflow_x {
Au(0)
} else {
model::specified(tx, kid.base.block_container_inline_size)
};
let ty = if let overflow_x::T::hidden = kid.fragment.style().get_box()
.overflow_y.0 {
Au(0)
} else {
model::specified(
ty,
kid.base.block_container_explicit_block_size.unwrap_or(Au(0))
)
};
let off = Point2D::new(tx, ty);
clip = clip.translate(&-off);
}
_ => {}
};
}
}
clip
} else {
clip.clone()
};
flow::mut_base(kid).clip = clip;
flow::mut_base(kid).stacking_relative_position_of_display_port = flow::mut_base(kid).stacking_relative_position_of_display_port =
stacking_relative_position_of_display_port_for_children; stacking_relative_position_of_display_port_for_children;
} }

View file

@ -1461,15 +1461,15 @@ impl FragmentDisplayListBuilding for Fragment {
return return
} }
let tmp; let overflow_clip_rect_owner;
let overflow_clip_rect = match self.style.get_box()._servo_overflow_clip_box { let overflow_clip_rect = match self.style.get_box()._servo_overflow_clip_box {
overflow_clip_box::T::padding_box => { overflow_clip_box::T::padding_box => {
// FIXME(SimonSapin): should be the padding box, not border box. // FIXME(SimonSapin): should be the padding box, not border box.
stacking_relative_border_box stacking_relative_border_box
} }
overflow_clip_box::T::content_box => { overflow_clip_box::T::content_box => {
tmp = self.stacking_relative_content_box(stacking_relative_border_box); overflow_clip_rect_owner = self.stacking_relative_content_box(stacking_relative_border_box);
&tmp &overflow_clip_rect_owner
} }
}; };
@ -1743,46 +1743,13 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
}; };
// Add the box that starts the block context. // Add the box that starts the block context.
let mut clip = self.base.clip.clone(); let clip_owner;
let clip = if establishes_stacking_context {
if establishes_stacking_context { clip_owner = self.base.clip.translate(&-self.base.stacking_relative_position);
clip = clip.translate(&-self.base.stacking_relative_position) &clip_owner
}
// TODO(notriddle): To properly support transformations, we either need
// non-rectangular clipping regions in display lists, or clipping
// regions in terms of the parent coordinate system instead of the
// child coordinate system.
//
// This is a workaround for a common idiom of transform: translate().
if let Some(ref operations) = self.fragment.style().get_effects().transform.0 {
for operation in operations {
match *operation {
transform::ComputedOperation::Translate(tx, ty, _) => {
// N.B. When the clipping value comes from us, it
// shouldn't be transformed.
let tx = if let overflow_x::T::hidden = self.fragment.style().get_box()
.overflow_x {
Au(0)
} else { } else {
model::specified(tx, self.base.block_container_inline_size) &self.base.clip
}; };
let ty = if let overflow_x::T::hidden = self.fragment.style().get_box()
.overflow_y.0 {
Au(0)
} else {
model::specified(
ty,
self.base.block_container_explicit_block_size.unwrap_or(Au(0))
)
};
let off = Point2D::new(tx, ty);
clip = clip.translate(&-off);
}
_ => {}
};
}
}
self.fragment self.fragment
.build_display_list(state, .build_display_list(state,
@ -1795,7 +1762,7 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
.relative_containing_block_mode, .relative_containing_block_mode,
border_painting_mode, border_painting_mode,
background_border_section, background_border_section,
&clip, clip,
&self.base.stacking_relative_position_of_display_port); &self.base.stacking_relative_position_of_display_port);
self.base.build_display_items_for_debugging_tint(state, self.fragment.node); self.base.build_display_items_for_debugging_tint(state, self.fragment.node);

View file

@ -5043,6 +5043,18 @@
"url": "/_mozilla/css/translate_clip.html" "url": "/_mozilla/css/translate_clip.html"
} }
], ],
"css/translate_clip_nested.html": [
{
"path": "css/translate_clip_nested.html",
"references": [
[
"/_mozilla/css/translate_clip_nested_ref.html",
"=="
]
],
"url": "/_mozilla/css/translate_clip_nested.html"
}
],
"css/upper_id_attr.html": [ "css/upper_id_attr.html": [
{ {
"path": "css/upper_id_attr.html", "path": "css/upper_id_attr.html",
@ -11549,6 +11561,18 @@
"url": "/_mozilla/css/translate_clip.html" "url": "/_mozilla/css/translate_clip.html"
} }
], ],
"css/translate_clip_nested.html": [
{
"path": "css/translate_clip_nested.html",
"references": [
[
"/_mozilla/css/translate_clip_nested_ref.html",
"=="
]
],
"url": "/_mozilla/css/translate_clip_nested.html"
}
],
"css/upper_id_attr.html": [ "css/upper_id_attr.html": [
{ {
"path": "css/upper_id_attr.html", "path": "css/upper_id_attr.html",

View file

@ -0,0 +1,16 @@
<!doctype html>
<meta charset="utf-8">
<link rel="match" href="translate_clip_nested_ref.html">
<style>
div {
height: 200px;
width: 200px;
}
</style>
<div style="position: relative; overflow: hidden; background: red;">
<div style="transform: translateX(-100px);">
<div style="position: absolute; right: -100px; background: green;">
1 2 3 4 5 6 7 8 9 10 11 12 13
</div>
</div>
</div>

View file

@ -0,0 +1,13 @@
<!doctype html>
<meta charset="utf-8">
<style>
div {
height: 200px;
width: 200px;
}
</style>
<div style="position: relative; overflow: hidden; background: red;">
<div style="position: absolute; right: 0; background: green;">
1 2 3 4 5 6 7 8 9 10 11 12 13
</div>
</div>