mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
layout: Allow transforming inline replaced elements (#31833)
This requires passing through information about whether or not the element in question is replaced when checking to see if it's transformable and transitively all functions that make decisions about containing blocks. A new FragmentFlag is added to help track this -- it will be set on both the replaced items BoxFragment container as well as the Fragment for the replaced item itself. Fixes #31806.
This commit is contained in:
parent
15cb9dd5fc
commit
b8c82c1ab0
11 changed files with 114 additions and 35 deletions
|
@ -474,7 +474,7 @@ impl StackingContext {
|
|||
if effects.filter.0.is_empty() &&
|
||||
effects.opacity == 1.0 &&
|
||||
effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
|
||||
!style.has_transform_or_perspective()
|
||||
!style.has_transform_or_perspective(FragmentFlags::empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@ -896,7 +896,7 @@ struct ReferenceFrameData {
|
|||
|
||||
impl BoxFragment {
|
||||
fn get_stacking_context_type(&self) -> Option<StackingContextType> {
|
||||
if self.style.establishes_stacking_context() {
|
||||
if self.style.establishes_stacking_context(self.base.flags) {
|
||||
return Some(StackingContextType::RealStackingContext);
|
||||
}
|
||||
|
||||
|
@ -986,7 +986,7 @@ impl BoxFragment {
|
|||
// properties will be replaced before recursing into children.
|
||||
assert!(self
|
||||
.style
|
||||
.establishes_containing_block_for_all_descendants());
|
||||
.establishes_containing_block_for_all_descendants(self.base.flags));
|
||||
let adjusted_containing_block = ContainingBlock::new(
|
||||
containing_block
|
||||
.rect
|
||||
|
@ -1168,7 +1168,7 @@ impl BoxFragment {
|
|||
// absolute and fixed descendants.
|
||||
let new_containing_block_info = if self
|
||||
.style
|
||||
.establishes_containing_block_for_all_descendants()
|
||||
.establishes_containing_block_for_all_descendants(self.base.flags)
|
||||
{
|
||||
containing_block_info.new_for_absolute_and_fixed_descendants(
|
||||
&for_non_absolute_descendants,
|
||||
|
@ -1176,7 +1176,7 @@ impl BoxFragment {
|
|||
)
|
||||
} else if self
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants()
|
||||
.establishes_containing_block_for_absolute_descendants(self.base.flags)
|
||||
{
|
||||
containing_block_info.new_for_absolute_descendants(
|
||||
&for_non_absolute_descendants,
|
||||
|
@ -1389,7 +1389,7 @@ impl BoxFragment {
|
|||
&self,
|
||||
containing_block_rect: &PhysicalRect<Length>,
|
||||
) -> Option<ReferenceFrameData> {
|
||||
if !self.style.has_transform_or_perspective() {
|
||||
if !self.style.has_transform_or_perspective(self.base.flags) {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::dom::NodeExt;
|
|||
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
||||
use crate::flexbox::FlexContainer;
|
||||
use crate::flow::BlockFormattingContext;
|
||||
use crate::fragment_tree::{BaseFragmentInfo, Fragment};
|
||||
use crate::fragment_tree::{BaseFragmentInfo, Fragment, FragmentFlags};
|
||||
use crate::positioned::PositioningContext;
|
||||
use crate::replaced::ReplacedContent;
|
||||
use crate::sizing::{self, ContentSizes};
|
||||
|
@ -131,11 +131,15 @@ impl IndependentFormattingContext {
|
|||
contents,
|
||||
})
|
||||
},
|
||||
Err(contents) => Self::Replaced(ReplacedFormattingContext {
|
||||
base_fragment_info: node_and_style_info.into(),
|
||||
style: Arc::clone(&node_and_style_info.style),
|
||||
contents,
|
||||
}),
|
||||
Err(contents) => {
|
||||
let mut base_fragment_info: BaseFragmentInfo = node_and_style_info.into();
|
||||
base_fragment_info.flags.insert(FragmentFlags::IS_REPLACED);
|
||||
Self::Replaced(ReplacedFormattingContext {
|
||||
base_fragment_info,
|
||||
style: Arc::clone(&node_and_style_info.style),
|
||||
contents,
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -88,6 +88,9 @@ bitflags! {
|
|||
const IS_BODY_ELEMENT_OF_HTML_ELEMENT_ROOT = 0b00000001;
|
||||
/// Whether or not the node that created this Fragment is a `<br>` element.
|
||||
const IS_BR_ELEMENT = 0b00000010;
|
||||
/// Whether or not this Fragment was created to contain a replaced element or is
|
||||
/// a replaced element.
|
||||
const IS_REPLACED = 0b00000100;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -192,12 +192,12 @@ impl Fragment {
|
|||
.translate(containing_block.origin.to_vector());
|
||||
let new_manager = if fragment
|
||||
.style
|
||||
.establishes_containing_block_for_all_descendants()
|
||||
.establishes_containing_block_for_all_descendants(fragment.base.flags)
|
||||
{
|
||||
manager.new_for_absolute_and_fixed_descendants(&content_rect, &padding_rect)
|
||||
} else if fragment
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants()
|
||||
.establishes_containing_block_for_absolute_descendants(fragment.base.flags)
|
||||
{
|
||||
manager.new_for_absolute_descendants(&content_rect, &padding_rect)
|
||||
} else {
|
||||
|
|
|
@ -18,7 +18,8 @@ use crate::dom::NodeExt;
|
|||
use crate::dom_traversal::{Contents, NodeAndStyleInfo};
|
||||
use crate::formatting_contexts::IndependentFormattingContext;
|
||||
use crate::fragment_tree::{
|
||||
AbsoluteBoxOffsets, BoxFragment, CollapsedBlockMargins, Fragment, HoistedSharedFragment,
|
||||
AbsoluteBoxOffsets, BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags,
|
||||
HoistedSharedFragment,
|
||||
};
|
||||
use crate::geom::{
|
||||
AuOrAuto, LengthOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2,
|
||||
|
@ -144,9 +145,13 @@ impl PositioningContext {
|
|||
}
|
||||
|
||||
pub(crate) fn new_for_style(style: &ComputedValues) -> Option<Self> {
|
||||
if style.establishes_containing_block_for_all_descendants() {
|
||||
// NB: We never make PositioningContexts for replaced elements, which is why we always
|
||||
// pass false here.
|
||||
if style.establishes_containing_block_for_all_descendants(FragmentFlags::empty()) {
|
||||
Some(Self::new_for_containing_block_for_all_descendants())
|
||||
} else if style.establishes_containing_block_for_absolute_descendants() {
|
||||
} else if style
|
||||
.establishes_containing_block_for_absolute_descendants(FragmentFlags::empty())
|
||||
{
|
||||
Some(Self {
|
||||
for_nearest_positioned_ancestor: Some(Vec::new()),
|
||||
for_nearest_containing_block_for_all_descendants: Vec::new(),
|
||||
|
|
|
@ -20,6 +20,7 @@ use style::Zero;
|
|||
use webrender_api as wr;
|
||||
|
||||
use crate::dom_traversal::Contents;
|
||||
use crate::fragment_tree::FragmentFlags;
|
||||
use crate::geom::{
|
||||
AuOrAuto, LengthOrAuto, LengthPercentageOrAuto, LogicalSides, LogicalVec2, PhysicalSides,
|
||||
PhysicalSize,
|
||||
|
@ -179,13 +180,19 @@ pub(crate) trait ComputedValuesExt {
|
|||
&self,
|
||||
containing_block_writing_mode: WritingMode,
|
||||
) -> LogicalSides<LengthPercentageOrAuto<'_>>;
|
||||
fn has_transform_or_perspective(&self) -> bool;
|
||||
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool;
|
||||
fn effective_z_index(&self) -> i32;
|
||||
fn establishes_block_formatting_context(&self) -> bool;
|
||||
fn establishes_stacking_context(&self) -> bool;
|
||||
fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool;
|
||||
fn establishes_scroll_container(&self) -> bool;
|
||||
fn establishes_containing_block_for_absolute_descendants(&self) -> bool;
|
||||
fn establishes_containing_block_for_all_descendants(&self) -> bool;
|
||||
fn establishes_containing_block_for_absolute_descendants(
|
||||
&self,
|
||||
fragment_flags: FragmentFlags,
|
||||
) -> bool;
|
||||
fn establishes_containing_block_for_all_descendants(
|
||||
&self,
|
||||
fragment_flags: FragmentFlags,
|
||||
) -> bool;
|
||||
fn background_is_transparent(&self) -> bool;
|
||||
fn get_webrender_primitive_flags(&self) -> wr::PrimitiveFlags;
|
||||
}
|
||||
|
@ -411,7 +418,7 @@ impl ComputedValuesExt for ComputedValues {
|
|||
|
||||
/// Returns true if this style has a transform, or perspective property set and
|
||||
/// it applies to this element.
|
||||
fn has_transform_or_perspective(&self) -> bool {
|
||||
fn has_transform_or_perspective(&self, fragment_flags: FragmentFlags) -> bool {
|
||||
// "A transformable element is an element in one of these categories:
|
||||
// * all elements whose layout is governed by the CSS box model except for
|
||||
// non-replaced inline boxes, table-column boxes, and table-column-group
|
||||
|
@ -420,8 +427,9 @@ impl ComputedValuesExt for ComputedValues {
|
|||
// elements with the exception of any descendant element of text content
|
||||
// elements."
|
||||
// https://drafts.csswg.org/css-transforms/#transformable-element
|
||||
// FIXME(mrobinson): Properly handle tables and replaced elements here.
|
||||
if self.get_box().display.is_inline_flow() {
|
||||
if self.get_box().display.is_inline_flow() &&
|
||||
!fragment_flags.contains(FragmentFlags::IS_REPLACED)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -464,7 +472,7 @@ impl ComputedValuesExt for ComputedValues {
|
|||
}
|
||||
|
||||
/// Returns true if this fragment establishes a new stacking context and false otherwise.
|
||||
fn establishes_stacking_context(&self) -> bool {
|
||||
fn establishes_stacking_context(&self, fragment_flags: FragmentFlags) -> bool {
|
||||
let effects = self.get_effects();
|
||||
if effects.opacity != 1.0 {
|
||||
return true;
|
||||
|
@ -474,7 +482,7 @@ impl ComputedValuesExt for ComputedValues {
|
|||
return true;
|
||||
}
|
||||
|
||||
if self.has_transform_or_perspective() {
|
||||
if self.has_transform_or_perspective(fragment_flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -513,8 +521,11 @@ impl ComputedValuesExt for ComputedValues {
|
|||
/// descendants) this method will return true, but a true return value does
|
||||
/// not imply that the style establishes a containing block for all descendants.
|
||||
/// Use `establishes_containing_block_for_all_descendants()` instead.
|
||||
fn establishes_containing_block_for_absolute_descendants(&self) -> bool {
|
||||
if self.establishes_containing_block_for_all_descendants() {
|
||||
fn establishes_containing_block_for_absolute_descendants(
|
||||
&self,
|
||||
fragment_flags: FragmentFlags,
|
||||
) -> bool {
|
||||
if self.establishes_containing_block_for_all_descendants(fragment_flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -525,8 +536,11 @@ impl ComputedValuesExt for ComputedValues {
|
|||
/// all descendants, including fixed descendants (`position: fixed`).
|
||||
/// Note that this also implies that it establishes a containing block
|
||||
/// for absolute descendants (`position: absolute`).
|
||||
fn establishes_containing_block_for_all_descendants(&self) -> bool {
|
||||
if self.has_transform_or_perspective() {
|
||||
fn establishes_containing_block_for_all_descendants(
|
||||
&self,
|
||||
fragment_flags: FragmentFlags,
|
||||
) -> bool {
|
||||
if self.has_transform_or_perspective(fragment_flags) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ use super::{Table, TableSlot, TableSlotCell, TableTrack, TableTrackGroup};
|
|||
use crate::context::LayoutContext;
|
||||
use crate::formatting_contexts::{Baselines, IndependentLayout};
|
||||
use crate::fragment_tree::{
|
||||
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment,
|
||||
BaseFragmentInfo, BoxFragment, CollapsedBlockMargins, ExtraBackground, Fragment, FragmentFlags,
|
||||
PositioningFragment,
|
||||
};
|
||||
use crate::geom::{AuOrAuto, LengthPercentageOrAuto, LogicalRect, LogicalSides, LogicalVec2};
|
||||
|
@ -985,11 +985,15 @@ impl<'a> TableLayout<'a> {
|
|||
row.group_index.map_or(false, |group_index| {
|
||||
self.table.row_groups[group_index]
|
||||
.style
|
||||
.establishes_containing_block_for_absolute_descendants()
|
||||
.establishes_containing_block_for_absolute_descendants(
|
||||
FragmentFlags::empty(),
|
||||
)
|
||||
});
|
||||
row_group_collects_for_nearest_positioned_ancestor ||
|
||||
row.style
|
||||
.establishes_containing_block_for_absolute_descendants()
|
||||
.establishes_containing_block_for_absolute_descendants(
|
||||
FragmentFlags::empty(),
|
||||
)
|
||||
});
|
||||
|
||||
let mut cells_laid_out_row = Vec::new();
|
||||
|
|
|
@ -260846,6 +260846,19 @@
|
|||
{}
|
||||
]
|
||||
],
|
||||
"transform-iframe-002.html": [
|
||||
"b9b10ea368324efa93e73476fc8084dadb89f2d0",
|
||||
[
|
||||
null,
|
||||
[
|
||||
[
|
||||
"/css/reference/ref-filled-green-100px-square-only.html",
|
||||
"=="
|
||||
]
|
||||
],
|
||||
{}
|
||||
]
|
||||
],
|
||||
"transform-image-001.html": [
|
||||
"0565b8dbeeb86b82993847a139c8f38b66c0b163",
|
||||
[
|
||||
|
@ -415399,6 +415412,10 @@
|
|||
"d92c3705b6b9dd270d17c64fce9010a5ab1fe4f3",
|
||||
[]
|
||||
],
|
||||
"transform-iframe-002-contents.html": [
|
||||
"84f079c90bcb590e81ba39753edf723bcb123858",
|
||||
[]
|
||||
],
|
||||
"transform-lime-square.png": [
|
||||
"8f939993332e1101b921615723ec6067f3bb90a3",
|
||||
[]
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
[transform3d-image-scale-001.html]
|
||||
expected: FAIL
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSS Test (Transforms): Iframe (contents)</title>
|
||||
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
|
||||
<style>
|
||||
body {
|
||||
background: green;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
20
tests/wpt/tests/css/css-transforms/transform-iframe-002.html
Normal file
20
tests/wpt/tests/css/css-transforms/transform-iframe-002.html
Normal file
|
@ -0,0 +1,20 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>CSS Test (Transforms): Iframe</title>
|
||||
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
|
||||
<link rel="help" href="http://www.w3.org/TR/css-transforms-1/#transform-rendering">
|
||||
<meta name="assert" content="This test ensures that an iframe element can be transformed.">
|
||||
<link rel="match" href="/css/reference/ref-filled-green-100px-square-only.html">
|
||||
<style>
|
||||
iframe {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
transform: translate(25px, 25px) scale(2, 2);
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<p>Test passes if there is a filled green square.</p>
|
||||
<iframe src="support/transform-iframe-002-contents.html"></iframe>
|
||||
</body>
|
||||
</html>
|
Loading…
Add table
Add a link
Reference in a new issue