layout_2020: Add support for transform-style

This requires creating a matching stacking context for every reference
frame and also properly placing those stacking contexts inside them.
This commit is contained in:
Martin Robinson 2020-05-11 14:56:35 +02:00
parent 4d541e8e38
commit a637810df3
4 changed files with 93 additions and 66 deletions

View file

@ -126,6 +126,10 @@ pub(crate) enum StackingContextType {
} }
pub(crate) struct StackingContext { pub(crate) struct StackingContext {
/// The spatial id of this fragment. This is used to properly handle
/// things like preserve-3d.
spatial_id: wr::SpatialId,
/// The fragment that established this stacking context. /// The fragment that established this stacking context.
initializing_fragment_style: Option<ServoArc<ComputedValues>>, initializing_fragment_style: Option<ServoArc<ComputedValues>>,
@ -145,10 +149,12 @@ pub(crate) struct StackingContext {
impl StackingContext { impl StackingContext {
pub(crate) fn new( pub(crate) fn new(
spatial_id: wr::SpatialId,
initializing_fragment_style: ServoArc<ComputedValues>, initializing_fragment_style: ServoArc<ComputedValues>,
context_type: StackingContextType, context_type: StackingContextType,
) -> Self { ) -> Self {
Self { Self {
spatial_id,
initializing_fragment_style: Some(initializing_fragment_style), initializing_fragment_style: Some(initializing_fragment_style),
context_type, context_type,
fragments: vec![], fragments: vec![],
@ -157,8 +163,9 @@ impl StackingContext {
} }
} }
pub(crate) fn create_root() -> Self { pub(crate) fn create_root(wr: &wr::DisplayListBuilder) -> Self {
Self { Self {
spatial_id: wr::SpaceAndClipInfo::root_scroll(wr.pipeline_id).spatial_id,
initializing_fragment_style: None, initializing_fragment_style: None,
context_type: StackingContextType::Real, context_type: StackingContextType::Real,
fragments: vec![], fragments: vec![],
@ -198,16 +205,18 @@ impl StackingContext {
&self, &self,
builder: &'a mut DisplayListBuilder, builder: &'a mut DisplayListBuilder,
) -> bool { ) -> bool {
let effects = match self.initializing_fragment_style.as_ref() { let style = match self.initializing_fragment_style.as_ref() {
Some(style) => style.get_effects(), Some(style) => style,
None => return false, None => return false,
}; };
// WebRender only uses the stacking context to apply certain effects. If we don't // WebRender only uses the stacking context to apply certain effects. If we don't
// actually need to create a stacking context, just avoid creating one. // actually need to create a stacking context, just avoid creating one.
let effects = style.get_effects();
if effects.filter.0.is_empty() && if effects.filter.0.is_empty() &&
effects.opacity == 1.0 && effects.opacity == 1.0 &&
effects.mix_blend_mode == ComputedMixBlendMode::Normal effects.mix_blend_mode == ComputedMixBlendMode::Normal &&
!style.has_transform_or_perspective()
{ {
return false; return false;
} }
@ -228,10 +237,10 @@ impl StackingContext {
builder.wr.push_stacking_context( builder.wr.push_stacking_context(
LayoutPoint::zero(), // origin LayoutPoint::zero(), // origin
builder.current_space_and_clip.spatial_id, // spatial_id self.spatial_id,
wr::PrimitiveFlags::default(), wr::PrimitiveFlags::default(),
None, // clip_id None, // clip_id
wr::TransformStyle::Flat, style.get_used_transform_style().to_webrender(),
effects.mix_blend_mode.to_webrender(), effects.mix_blend_mode.to_webrender(),
&filters, &filters,
&vec![], // filter_datas &vec![], // filter_datas
@ -423,8 +432,16 @@ impl BoxFragment {
builder.clipping_and_scrolling_scope(|builder| { builder.clipping_and_scrolling_scope(|builder| {
self.adjust_spatial_id_for_positioning(builder); self.adjust_spatial_id_for_positioning(builder);
let context_type = match self.get_stacking_context_type() { match self.get_stacking_context_type() {
Some(context_type) => context_type, Some(context_type) => {
self.build_stacking_context_tree_creating_stacking_context(
fragment,
builder,
containing_block_info,
stacking_context,
context_type,
);
},
None => { None => {
self.build_stacking_context_tree_for_children( self.build_stacking_context_tree_for_children(
fragment, fragment,
@ -432,15 +449,48 @@ impl BoxFragment {
containing_block_info, containing_block_info,
stacking_context, stacking_context,
); );
return;
}, },
}; }
});
}
let mut child_stacking_context = StackingContext::new(self.style.clone(), context_type); fn build_stacking_context_tree_creating_stacking_context(
&self,
fragment: &ArcRefCell<Fragment>,
builder: &mut StackingContextBuilder,
containing_block_info: &ContainingBlockInfo,
parent_stacking_context: &mut StackingContext,
context_type: StackingContextType,
) {
// If we are creating a stacking context, we may also need to create a reference
// frame first.
let relative_border_rect = self
.border_rect()
.to_physical(self.style.writing_mode, &containing_block_info.rect);
let border_rect =
relative_border_rect.translate(containing_block_info.rect.origin.to_vector());
let established_reference_frame =
self.build_reference_frame_if_necessary(builder, &border_rect);
// WebRender reference frames establish a new coordinate system at their origin
// (the border box of the fragment). We need to ensure that any coordinates we
// give to WebRender in this reference frame are relative to the fragment border
// box. We do this by adjusting the containing block origin.
let mut new_containing_block_info = containing_block_info.clone();
if established_reference_frame {
new_containing_block_info.rect.origin =
(-relative_border_rect.origin.to_vector()).to_point();
}
let mut child_stacking_context = StackingContext::new(
builder.current_space_and_clip.spatial_id,
self.style.clone(),
context_type,
);
self.build_stacking_context_tree_for_children( self.build_stacking_context_tree_for_children(
fragment, fragment,
builder, builder,
containing_block_info, &new_containing_block_info,
&mut child_stacking_context, &mut child_stacking_context,
); );
@ -453,13 +503,16 @@ impl BoxFragment {
} }
child_stacking_context.sort(); child_stacking_context.sort();
stacking_context parent_stacking_context
.stacking_contexts .stacking_contexts
.push(child_stacking_context); .push(child_stacking_context);
stacking_context parent_stacking_context
.stacking_contexts .stacking_contexts
.append(&mut stolen_children); .append(&mut stolen_children);
});
if established_reference_frame {
builder.wr.pop_reference_frame();
}
} }
fn build_stacking_context_tree_for_children<'a>( fn build_stacking_context_tree_for_children<'a>(
@ -469,40 +522,22 @@ impl BoxFragment {
containing_block_info: &ContainingBlockInfo, containing_block_info: &ContainingBlockInfo,
stacking_context: &mut StackingContext, stacking_context: &mut StackingContext,
) { ) {
let relative_border_rect = self
.border_rect()
.to_physical(self.style.writing_mode, &containing_block_info.rect);
let border_rect =
relative_border_rect.translate(containing_block_info.rect.origin.to_vector());
let established_reference_frame =
self.build_reference_frame_if_necessary(builder, &border_rect);
let mut new_containing_block_info = containing_block_info.clone();
// WebRender reference frames establish a new coordinate system at their origin
// (the border box of the fragment). We need to ensure that any coordinates we
// give to WebRender in this reference frame are relative to the fragment border
// box. We do this by adjusting the containing block origin.
if established_reference_frame {
new_containing_block_info.rect.origin =
(-relative_border_rect.origin.to_vector()).to_point();
}
stacking_context.fragments.push(StackingContextFragment { stacking_context.fragments.push(StackingContextFragment {
space_and_clip: builder.current_space_and_clip, space_and_clip: builder.current_space_and_clip,
section: self.get_stacking_context_section(), section: self.get_stacking_context_section(),
containing_block: new_containing_block_info.rect, containing_block: containing_block_info.rect,
fragment: fragment.clone(), fragment: fragment.clone(),
}); });
// We want to build the scroll frame after the background and border, because // We want to build the scroll frame after the background and border, because
// they shouldn't scroll with the rest of the box content. // they shouldn't scroll with the rest of the box content.
self.build_scroll_frame_if_necessary(builder, &new_containing_block_info); self.build_scroll_frame_if_necessary(builder, containing_block_info);
let padding_rect = self let padding_rect = self
.padding_rect() .padding_rect()
.to_physical(self.style.writing_mode, &new_containing_block_info.rect) .to_physical(self.style.writing_mode, &containing_block_info.rect)
.translate(new_containing_block_info.rect.origin.to_vector()); .translate(containing_block_info.rect.origin.to_vector());
let mut new_containing_block_info = containing_block_info.clone();
new_containing_block_info.rect = self new_containing_block_info.rect = self
.content_rect .content_rect
.to_physical(self.style.writing_mode, &new_containing_block_info.rect) .to_physical(self.style.writing_mode, &new_containing_block_info.rect)
@ -522,10 +557,6 @@ impl BoxFragment {
StackingContextBuildMode::SkipHoisted, StackingContextBuildMode::SkipHoisted,
); );
} }
if established_reference_frame {
builder.wr.pop_reference_frame();
}
} }
fn adjust_spatial_id_for_positioning(&self, builder: &mut StackingContextBuilder) { fn adjust_spatial_id_for_positioning(&self, builder: &mut StackingContextBuilder) {

View file

@ -196,7 +196,7 @@ impl BoxTreeRoot {
impl FragmentTreeRoot { impl FragmentTreeRoot {
pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) { pub fn build_display_list(&self, builder: &mut crate::display_list::DisplayListBuilder) {
let mut stacking_context = StackingContext::create_root(); let mut stacking_context = StackingContext::create_root(&builder.wr);
{ {
let mut stacking_context_builder = StackingContextBuilder::new(&mut builder.wr); let mut stacking_context_builder = StackingContextBuilder::new(&mut builder.wr);
let containing_block_info = ContainingBlockInfo { let containing_block_info = ContainingBlockInfo {

View file

@ -1,2 +0,0 @@
[css-transform-3d-transform-style.html]
expected: FAIL

View file

@ -1,2 +0,0 @@
[transform3d-sorting-005.html]
expected: FAIL