mirror of
https://github.com/servo/servo.git
synced 2025-08-03 20:50:07 +01:00
layout: Implement opacity
per CSS-COLOR § 3.2.
This adds the infrastructure necessary to support stacking contexts that are not containing blocks for absolutely-positioned elements. Our infrastructure did not support that before. This minor revamp actually ended up simplifying the logic around display list building and stacking-relative position computation for absolutely-positioned flows, which was nice.
This commit is contained in:
parent
873ca6cadd
commit
1c1c507c03
17 changed files with 421 additions and 146 deletions
|
@ -146,6 +146,8 @@ pub struct StackingContext {
|
||||||
pub clip_rect: Rect<Au>,
|
pub clip_rect: Rect<Au>,
|
||||||
/// The `z-index` for this stacking context.
|
/// The `z-index` for this stacking context.
|
||||||
pub z_index: i32,
|
pub z_index: i32,
|
||||||
|
/// The opacity of this stacking context.
|
||||||
|
pub opacity: AzFloat,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StackingContext {
|
impl StackingContext {
|
||||||
|
@ -157,6 +159,7 @@ impl StackingContext {
|
||||||
pub fn new(display_list: Box<DisplayList>,
|
pub fn new(display_list: Box<DisplayList>,
|
||||||
bounds: Rect<Au>,
|
bounds: Rect<Au>,
|
||||||
z_index: i32,
|
z_index: i32,
|
||||||
|
opacity: AzFloat,
|
||||||
layer: Option<Arc<RenderLayer>>)
|
layer: Option<Arc<RenderLayer>>)
|
||||||
-> StackingContext {
|
-> StackingContext {
|
||||||
StackingContext {
|
StackingContext {
|
||||||
|
@ -165,6 +168,7 @@ impl StackingContext {
|
||||||
bounds: bounds,
|
bounds: bounds,
|
||||||
clip_rect: bounds,
|
clip_rect: bounds,
|
||||||
z_index: z_index,
|
z_index: z_index,
|
||||||
|
opacity: opacity,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,82 +178,107 @@ impl StackingContext {
|
||||||
tile_bounds: &Rect<AzFloat>,
|
tile_bounds: &Rect<AzFloat>,
|
||||||
current_transform: &Matrix2D<AzFloat>,
|
current_transform: &Matrix2D<AzFloat>,
|
||||||
current_clip_stack: &mut Vec<Rect<Au>>) {
|
current_clip_stack: &mut Vec<Rect<Au>>) {
|
||||||
// Optimize the display list to throw out out-of-bounds display items and so forth.
|
let temporary_draw_target =
|
||||||
let display_list = DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
|
render_context.get_or_create_temporary_draw_target(self.opacity);
|
||||||
|
{
|
||||||
|
let mut render_subcontext = RenderContext {
|
||||||
|
draw_target: temporary_draw_target.clone(),
|
||||||
|
font_ctx: &mut *render_context.font_ctx,
|
||||||
|
page_rect: render_context.page_rect,
|
||||||
|
screen_rect: render_context.screen_rect,
|
||||||
|
..*render_context
|
||||||
|
};
|
||||||
|
|
||||||
// Sort positioned children according to z-index.
|
// Optimize the display list to throw out out-of-bounds display items and so forth.
|
||||||
let mut positioned_children = SmallVec8::new();
|
let display_list =
|
||||||
for kid in display_list.children.iter() {
|
DisplayListOptimizer::new(tile_bounds).optimize(&*self.display_list);
|
||||||
positioned_children.push((*kid).clone());
|
|
||||||
}
|
|
||||||
positioned_children.as_slice_mut().sort_by(|this, other| this.z_index.cmp(&other.z_index));
|
|
||||||
|
|
||||||
// Steps 1 and 2: Borders and background for the root.
|
// Sort positioned children according to z-index.
|
||||||
for display_item in display_list.background_and_borders.iter() {
|
let mut positioned_children = SmallVec8::new();
|
||||||
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
|
for kid in display_list.children.iter() {
|
||||||
}
|
positioned_children.push((*kid).clone());
|
||||||
|
|
||||||
// Step 3: Positioned descendants with negative z-indices.
|
|
||||||
for positioned_kid in positioned_children.iter() {
|
|
||||||
if positioned_kid.z_index >= 0 {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
if positioned_kid.layer.is_none() {
|
positioned_children.as_slice_mut()
|
||||||
let new_transform =
|
.sort_by(|this, other| this.z_index.cmp(&other.z_index));
|
||||||
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
|
|
||||||
as AzFloat,
|
|
||||||
positioned_kid.bounds.origin.y.to_nearest_px()
|
|
||||||
as AzFloat);
|
|
||||||
let new_tile_rect =
|
|
||||||
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
|
|
||||||
&**positioned_kid);
|
|
||||||
positioned_kid.optimize_and_draw_into_context(render_context,
|
|
||||||
&new_tile_rect,
|
|
||||||
&new_transform,
|
|
||||||
current_clip_stack);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 4: Block backgrounds and borders.
|
// Steps 1 and 2: Borders and background for the root.
|
||||||
for display_item in display_list.block_backgrounds_and_borders.iter() {
|
for display_item in display_list.background_and_borders.iter() {
|
||||||
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
|
display_item.draw_into_context(&mut render_subcontext,
|
||||||
}
|
current_transform,
|
||||||
|
current_clip_stack)
|
||||||
// Step 5: Floats.
|
|
||||||
for display_item in display_list.floats.iter() {
|
|
||||||
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
|
|
||||||
|
|
||||||
// Step 7: Content.
|
|
||||||
for display_item in display_list.content.iter() {
|
|
||||||
display_item.draw_into_context(render_context, current_transform, current_clip_stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Steps 8 and 9: Positioned descendants with nonnegative z-indices.
|
|
||||||
for positioned_kid in positioned_children.iter() {
|
|
||||||
if positioned_kid.z_index < 0 {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if positioned_kid.layer.is_none() {
|
// Step 3: Positioned descendants with negative z-indices.
|
||||||
let new_transform =
|
for positioned_kid in positioned_children.iter() {
|
||||||
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
|
if positioned_kid.z_index >= 0 {
|
||||||
as AzFloat,
|
break
|
||||||
positioned_kid.bounds.origin.y.to_nearest_px()
|
}
|
||||||
as AzFloat);
|
if positioned_kid.layer.is_none() {
|
||||||
let new_tile_rect =
|
let new_transform =
|
||||||
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
|
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
|
||||||
&**positioned_kid);
|
as AzFloat,
|
||||||
positioned_kid.optimize_and_draw_into_context(render_context,
|
positioned_kid.bounds.origin.y.to_nearest_px()
|
||||||
&new_tile_rect,
|
as AzFloat);
|
||||||
&new_transform,
|
let new_tile_rect =
|
||||||
current_clip_stack);
|
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
|
||||||
|
&**positioned_kid);
|
||||||
|
positioned_kid.optimize_and_draw_into_context(&mut render_subcontext,
|
||||||
|
&new_tile_rect,
|
||||||
|
&new_transform,
|
||||||
|
current_clip_stack);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Step 4: Block backgrounds and borders.
|
||||||
|
for display_item in display_list.block_backgrounds_and_borders.iter() {
|
||||||
|
display_item.draw_into_context(&mut render_subcontext,
|
||||||
|
current_transform,
|
||||||
|
current_clip_stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5: Floats.
|
||||||
|
for display_item in display_list.floats.iter() {
|
||||||
|
display_item.draw_into_context(&mut render_subcontext,
|
||||||
|
current_transform,
|
||||||
|
current_clip_stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Step 6: Inlines that generate stacking contexts.
|
||||||
|
|
||||||
|
// Step 7: Content.
|
||||||
|
for display_item in display_list.content.iter() {
|
||||||
|
display_item.draw_into_context(&mut render_subcontext,
|
||||||
|
current_transform,
|
||||||
|
current_clip_stack)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Steps 8 and 9: Positioned descendants with nonnegative z-indices.
|
||||||
|
for positioned_kid in positioned_children.iter() {
|
||||||
|
if positioned_kid.z_index < 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if positioned_kid.layer.is_none() {
|
||||||
|
let new_transform =
|
||||||
|
current_transform.translate(positioned_kid.bounds.origin.x.to_nearest_px()
|
||||||
|
as AzFloat,
|
||||||
|
positioned_kid.bounds.origin.y.to_nearest_px()
|
||||||
|
as AzFloat);
|
||||||
|
let new_tile_rect =
|
||||||
|
self.compute_tile_rect_for_child_stacking_context(tile_bounds,
|
||||||
|
&**positioned_kid);
|
||||||
|
positioned_kid.optimize_and_draw_into_context(&mut render_subcontext,
|
||||||
|
&new_tile_rect,
|
||||||
|
&new_transform,
|
||||||
|
current_clip_stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(pcwalton): Step 10: Outlines.
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pcwalton): Step 10: Outlines.
|
render_context.draw_temporary_draw_target_if_necessary(&temporary_draw_target,
|
||||||
|
self.opacity)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Translate the given tile rect into the coordinate system of a child stacking context.
|
/// Translate the given tile rect into the coordinate system of a child stacking context.
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
//! Painting of display lists using Moz2D/Azure.
|
//! Painting of display lists using Moz2D/Azure.
|
||||||
|
|
||||||
|
use azure::azure::AzIntSize;
|
||||||
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
|
use azure::azure_hl::{B8G8R8A8, A8, Color, ColorPattern, ColorPatternRef, DrawOptions};
|
||||||
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, ExtendClamp, GradientStop, Linear};
|
use azure::azure_hl::{DrawSurfaceOptions, DrawTarget, ExtendClamp, GradientStop, Linear};
|
||||||
use azure::azure_hl::{LinearGradientPattern, LinearGradientPatternRef, SourceOp, StrokeOptions};
|
use azure::azure_hl::{LinearGradientPattern, LinearGradientPatternRef, SourceOp, StrokeOptions};
|
||||||
|
@ -33,7 +34,7 @@ use text::TextRun;
|
||||||
use text::glyph::CharIndex;
|
use text::glyph::CharIndex;
|
||||||
|
|
||||||
pub struct RenderContext<'a> {
|
pub struct RenderContext<'a> {
|
||||||
pub draw_target: &'a DrawTarget,
|
pub draw_target: DrawTarget,
|
||||||
pub font_ctx: &'a mut Box<FontContext>,
|
pub font_ctx: &'a mut Box<FontContext>,
|
||||||
/// The rectangle that this context encompasses in page coordinates.
|
/// The rectangle that this context encompasses in page coordinates.
|
||||||
pub page_rect: Rect<f32>,
|
pub page_rect: Rect<f32>,
|
||||||
|
@ -54,8 +55,8 @@ enum DashSize {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> RenderContext<'a> {
|
impl<'a> RenderContext<'a> {
|
||||||
pub fn get_draw_target(&self) -> &'a DrawTarget {
|
pub fn get_draw_target(&self) -> &DrawTarget {
|
||||||
self.draw_target
|
&self.draw_target
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
|
pub fn draw_solid_color(&self, bounds: &Rect<Au>, color: Color) {
|
||||||
|
@ -153,7 +154,13 @@ impl<'a> RenderContext<'a> {
|
||||||
self.draw_target.fill_rect(&rect, ColorPatternRef(&pattern), Some(&draw_options));
|
self.draw_target.fill_rect(&rect, ColorPatternRef(&pattern), Some(&draw_options));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw_border_segment(&self, direction: Direction, bounds: &Rect<Au>, border: SideOffsets2D<f32>, radius: &BorderRadii<AzFloat>, color: SideOffsets2D<Color>, style: SideOffsets2D<border_style::T>) {
|
fn draw_border_segment(&self,
|
||||||
|
direction: Direction,
|
||||||
|
bounds: &Rect<Au>,
|
||||||
|
border: SideOffsets2D<f32>,
|
||||||
|
radius: &BorderRadii<AzFloat>,
|
||||||
|
color: SideOffsets2D<Color>,
|
||||||
|
style: SideOffsets2D<border_style::T>) {
|
||||||
let (style_select, color_select) = match direction {
|
let (style_select, color_select) = match direction {
|
||||||
Top => (style.top, color.top),
|
Top => (style.top, color.top),
|
||||||
Left => (style.left, color.left),
|
Left => (style.left, color.left),
|
||||||
|
@ -641,6 +648,49 @@ impl<'a> RenderContext<'a> {
|
||||||
LinearGradientPatternRef(&pattern),
|
LinearGradientPatternRef(&pattern),
|
||||||
None);
|
None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_or_create_temporary_draw_target(&mut self, opacity: AzFloat) -> DrawTarget {
|
||||||
|
if opacity == 1.0 {
|
||||||
|
return self.draw_target.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME(pcwalton): This surface might be bigger than necessary and waste memory.
|
||||||
|
let size = self.draw_target.get_size();
|
||||||
|
let size = Size2D {
|
||||||
|
width: size.width,
|
||||||
|
height: size.height,
|
||||||
|
};
|
||||||
|
|
||||||
|
let temporary_draw_target =
|
||||||
|
self.draw_target.create_similar_draw_target(&size, self.draw_target.get_format());
|
||||||
|
temporary_draw_target.set_transform(&self.draw_target.get_transform());
|
||||||
|
temporary_draw_target
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If we created a temporary draw target, then draw it to the main draw target. This is called
|
||||||
|
/// after doing all the painting, and the temporary draw target must not be used afterward.
|
||||||
|
pub fn draw_temporary_draw_target_if_necessary(&mut self,
|
||||||
|
temporary_draw_target: &DrawTarget,
|
||||||
|
opacity: AzFloat) {
|
||||||
|
if (*temporary_draw_target) == self.draw_target {
|
||||||
|
// We're directly rendering to the surface; nothing to do.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let old_transform = self.draw_target.get_transform();
|
||||||
|
self.draw_target.set_transform(&Matrix2D::identity());
|
||||||
|
temporary_draw_target.set_transform(&Matrix2D::identity());
|
||||||
|
let rect = Rect(Point2D(0.0, 0.0), self.draw_target.get_size().to_azure_size());
|
||||||
|
let source_surface = temporary_draw_target.snapshot();
|
||||||
|
let draw_surface_options = DrawSurfaceOptions::new(Linear, true);
|
||||||
|
let draw_options = DrawOptions::new(opacity, 0);
|
||||||
|
self.draw_target.draw_surface(source_surface,
|
||||||
|
rect,
|
||||||
|
rect,
|
||||||
|
draw_surface_options,
|
||||||
|
draw_options);
|
||||||
|
self.draw_target.set_transform(&old_transform);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait ToAzurePoint {
|
pub trait ToAzurePoint {
|
||||||
|
@ -665,6 +715,16 @@ impl ToAzureRect for Rect<Au> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait ToAzureSize {
|
||||||
|
fn to_azure_size(&self) -> Size2D<AzFloat>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToAzureSize for AzIntSize {
|
||||||
|
fn to_azure_size(&self) -> Size2D<AzFloat> {
|
||||||
|
Size2D(self.width as AzFloat, self.height as AzFloat)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
trait ToSideOffsetsPx {
|
trait ToSideOffsetsPx {
|
||||||
fn to_float_px(&self) -> SideOffsets2D<AzFloat>;
|
fn to_float_px(&self) -> SideOffsets2D<AzFloat>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -505,7 +505,7 @@ impl WorkerThread {
|
||||||
{
|
{
|
||||||
// Build the render context.
|
// Build the render context.
|
||||||
let mut render_context = RenderContext {
|
let mut render_context = RenderContext {
|
||||||
draw_target: &draw_target,
|
draw_target: draw_target.clone(),
|
||||||
font_ctx: &mut self.font_context,
|
font_ctx: &mut self.font_context,
|
||||||
page_rect: tile.page_rect,
|
page_rect: tile.page_rect,
|
||||||
screen_rect: tile.screen_rect,
|
screen_rect: tile.screen_rect,
|
||||||
|
|
|
@ -1722,20 +1722,31 @@ impl Flow for BlockFlow {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute absolute position info for children.
|
// Compute absolute position info for children.
|
||||||
|
let stacking_relative_position_of_absolute_containing_block_for_children =
|
||||||
|
if self.fragment.establishes_stacking_context() {
|
||||||
|
let logical_border_width = self.fragment.style().logical_border_width();
|
||||||
|
let position = LogicalPoint::new(self.base.writing_mode,
|
||||||
|
logical_border_width.inline_start,
|
||||||
|
logical_border_width.block_start);
|
||||||
|
let position = position.to_physical(self.base.writing_mode, container_size);
|
||||||
|
if self.is_positioned() {
|
||||||
|
position
|
||||||
|
} else {
|
||||||
|
// We establish a stacking context but are not positioned. (This will happen
|
||||||
|
// if, for example, the element has `position: static` but has `opacity` or
|
||||||
|
// `transform` set.) In this case, absolutely-positioned children will not be
|
||||||
|
// positioned relative to us but will instead be positioned relative to our
|
||||||
|
// containing block.
|
||||||
|
position - self.base.stacking_relative_position
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
self.base
|
||||||
|
.absolute_position_info
|
||||||
|
.stacking_relative_position_of_absolute_containing_block
|
||||||
|
};
|
||||||
let absolute_position_info_for_children = AbsolutePositionInfo {
|
let absolute_position_info_for_children = AbsolutePositionInfo {
|
||||||
stacking_relative_position_of_absolute_containing_block:
|
stacking_relative_position_of_absolute_containing_block:
|
||||||
if self.fragment.establishes_stacking_context() {
|
stacking_relative_position_of_absolute_containing_block_for_children,
|
||||||
let logical_border_width = self.fragment.style().logical_border_width();
|
|
||||||
LogicalPoint::new(self.base.writing_mode,
|
|
||||||
logical_border_width.inline_start,
|
|
||||||
logical_border_width.block_start).to_physical(
|
|
||||||
self.base.writing_mode,
|
|
||||||
container_size)
|
|
||||||
} else {
|
|
||||||
self.base
|
|
||||||
.absolute_position_info
|
|
||||||
.stacking_relative_position_of_absolute_containing_block
|
|
||||||
},
|
|
||||||
relative_containing_block_size: self.fragment.content_box().size,
|
relative_containing_block_size: self.fragment.content_box().size,
|
||||||
layers_needed_for_positioned_flows: self.base
|
layers_needed_for_positioned_flows: self.base
|
||||||
.flags
|
.flags
|
||||||
|
@ -1760,17 +1771,11 @@ impl Flow for BlockFlow {
|
||||||
origin_for_children +
|
origin_for_children +
|
||||||
(kid_base.position.start + relative_offset).to_physical(writing_mode,
|
(kid_base.position.start + relative_offset).to_physical(writing_mode,
|
||||||
container_size);
|
container_size);
|
||||||
kid_base.absolute_position_info = absolute_position_info_for_children
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flow::mut_base(kid).absolute_position_info = absolute_position_info_for_children;
|
||||||
flow::mut_base(kid).clip_rect = clip_rect
|
flow::mut_base(kid).clip_rect = clip_rect
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process absolute descendant links.
|
|
||||||
for absolute_descendant in self.base.abs_descendants.iter() {
|
|
||||||
flow::mut_base(absolute_descendant).absolute_position_info =
|
|
||||||
absolute_position_info_for_children
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mark_as_root(&mut self) {
|
fn mark_as_root(&mut self) {
|
||||||
|
|
|
@ -12,8 +12,7 @@
|
||||||
|
|
||||||
use block::BlockFlow;
|
use block::BlockFlow;
|
||||||
use context::LayoutContext;
|
use context::LayoutContext;
|
||||||
use flow::{mod, Flow};
|
use flow::{mod, Flow, NEEDS_LAYER};
|
||||||
use flow::{IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER};
|
|
||||||
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
|
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
|
||||||
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
|
||||||
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, TableFragment};
|
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, TableFragment};
|
||||||
|
@ -811,6 +810,10 @@ pub trait BlockFlowDisplayListBuilding {
|
||||||
fn build_display_list_for_absolutely_positioned_block(&mut self,
|
fn build_display_list_for_absolutely_positioned_block(&mut self,
|
||||||
layout_context: &LayoutContext);
|
layout_context: &LayoutContext);
|
||||||
fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext);
|
fn build_display_list_for_floating_block(&mut self, layout_context: &LayoutContext);
|
||||||
|
fn create_stacking_context(&self,
|
||||||
|
display_list: Box<DisplayList>,
|
||||||
|
layer: Option<Arc<RenderLayer>>)
|
||||||
|
-> Arc<StackingContext>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockFlowDisplayListBuilding for BlockFlow {
|
impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
|
@ -828,19 +831,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
&self.base.clip_rect);
|
&self.base.clip_rect);
|
||||||
|
|
||||||
for kid in self.base.children.iter_mut() {
|
for kid in self.base.children.iter_mut() {
|
||||||
if flow::base(kid).flags.contains(IS_ABSOLUTELY_POSITIONED) {
|
|
||||||
// All absolute flows will be handled by their containing block.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
flow::mut_base(kid).display_list_building_result.add_to(display_list);
|
flow::mut_base(kid).display_list_building_result.add_to(display_list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process absolute descendant links.
|
|
||||||
for abs_descendant_link in self.base.abs_descendants.iter() {
|
|
||||||
// TODO(pradeep): Send in our absolute position directly.
|
|
||||||
flow::mut_base(abs_descendant_link).display_list_building_result.add_to(display_list);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_display_list_for_block(&mut self,
|
fn build_display_list_for_block(&mut self,
|
||||||
|
@ -850,7 +842,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
self.build_display_list_for_block_base(&mut *display_list,
|
self.build_display_list_for_block_base(&mut *display_list,
|
||||||
layout_context,
|
layout_context,
|
||||||
background_border_level);
|
background_border_level);
|
||||||
self.base.display_list_building_result = DisplayListResult(display_list);
|
|
||||||
|
self.base.display_list_building_result = if self.fragment.establishes_stacking_context() {
|
||||||
|
StackingContextResult(self.create_stacking_context(display_list, None))
|
||||||
|
} else {
|
||||||
|
DisplayListResult(display_list)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_display_list_for_absolutely_positioned_block(&mut self,
|
fn build_display_list_for_absolutely_positioned_block(&mut self,
|
||||||
|
@ -860,18 +857,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
layout_context,
|
layout_context,
|
||||||
RootOfStackingContextLevel);
|
RootOfStackingContextLevel);
|
||||||
|
|
||||||
let bounds = Rect(self.base.stacking_relative_position,
|
|
||||||
self.base.overflow.size.to_physical(self.base.writing_mode));
|
|
||||||
let z_index = self.fragment.style().get_box().z_index.number_or_zero();
|
|
||||||
|
|
||||||
if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
|
if !self.base.absolute_position_info.layers_needed_for_positioned_flows &&
|
||||||
!self.base.flags.contains(NEEDS_LAYER) {
|
!self.base.flags.contains(NEEDS_LAYER) {
|
||||||
// We didn't need a layer.
|
// We didn't need a layer.
|
||||||
self.base.display_list_building_result =
|
self.base.display_list_building_result =
|
||||||
StackingContextResult(Arc::new(StackingContext::new(display_list,
|
StackingContextResult(self.create_stacking_context(display_list, None));
|
||||||
bounds,
|
|
||||||
z_index,
|
|
||||||
None)));
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -884,13 +874,10 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
|
|
||||||
let transparent = color::rgba(1.0, 1.0, 1.0, 0.0);
|
let transparent = color::rgba(1.0, 1.0, 1.0, 0.0);
|
||||||
let stacking_context =
|
let stacking_context =
|
||||||
Arc::new(StackingContext::new(display_list,
|
self.create_stacking_context(display_list,
|
||||||
bounds,
|
Some(Arc::new(RenderLayer::new(self.layer_id(0),
|
||||||
z_index,
|
transparent,
|
||||||
Some(Arc::new(RenderLayer::new(self.layer_id(0),
|
scroll_policy))));
|
||||||
transparent,
|
|
||||||
scroll_policy)))));
|
|
||||||
|
|
||||||
self.base.display_list_building_result = StackingContextResult(stacking_context)
|
self.base.display_list_building_result = StackingContextResult(stacking_context)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -900,7 +887,23 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
layout_context,
|
layout_context,
|
||||||
RootOfStackingContextLevel);
|
RootOfStackingContextLevel);
|
||||||
display_list.form_float_pseudo_stacking_context();
|
display_list.form_float_pseudo_stacking_context();
|
||||||
self.base.display_list_building_result = DisplayListResult(display_list);
|
|
||||||
|
self.base.display_list_building_result = if self.fragment.establishes_stacking_context() {
|
||||||
|
StackingContextResult(self.create_stacking_context(display_list, None))
|
||||||
|
} else {
|
||||||
|
DisplayListResult(display_list)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_stacking_context(&self,
|
||||||
|
display_list: Box<DisplayList>,
|
||||||
|
layer: Option<Arc<RenderLayer>>)
|
||||||
|
-> Arc<StackingContext> {
|
||||||
|
let bounds = Rect(self.base.stacking_relative_position,
|
||||||
|
self.base.overflow.size.to_physical(self.base.writing_mode));
|
||||||
|
let z_index = self.fragment.style().get_box().z_index.number_or_zero();
|
||||||
|
let opacity = self.fragment.style().get_effects().opacity as f32;
|
||||||
|
Arc::new(StackingContext::new(display_list, bounds, z_index, opacity, layer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1494,6 +1494,9 @@ impl Fragment {
|
||||||
|
|
||||||
/// Returns true if this fragment establishes a new stacking context and false otherwise.
|
/// Returns true if this fragment establishes a new stacking context and false otherwise.
|
||||||
pub fn establishes_stacking_context(&self) -> bool {
|
pub fn establishes_stacking_context(&self) -> bool {
|
||||||
|
if self.style().get_effects().opacity != 1.0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
match self.style().get_box().position {
|
match self.style().get_box().position {
|
||||||
position::absolute | position::fixed => {
|
position::absolute | position::fixed => {
|
||||||
// FIXME(pcwalton): This should only establish a new stacking context when
|
// FIXME(pcwalton): This should only establish a new stacking context when
|
||||||
|
|
|
@ -20,7 +20,7 @@ use model::IntrinsicISizesContribution;
|
||||||
use text;
|
use text;
|
||||||
|
|
||||||
use collections::{RingBuf};
|
use collections::{RingBuf};
|
||||||
use geom::{Rect, Size2D};
|
use geom::Size2D;
|
||||||
use gfx::display_list::DisplayList;
|
use gfx::display_list::DisplayList;
|
||||||
use gfx::font::FontMetrics;
|
use gfx::font::FontMetrics;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
|
@ -1141,9 +1141,10 @@ impl Flow for InlineFlow {
|
||||||
let stacking_relative_position = match fragment.specific {
|
let stacking_relative_position = match fragment.specific {
|
||||||
InlineBlockFragment(ref mut info) => {
|
InlineBlockFragment(ref mut info) => {
|
||||||
let block_flow = info.flow_ref.as_block();
|
let block_flow = info.flow_ref.as_block();
|
||||||
|
block_flow.base.absolute_position_info = self.base.absolute_position_info;
|
||||||
|
|
||||||
// FIXME(#2795): Get the real container size
|
// FIXME(#2795): Get the real container size
|
||||||
let container_size = Size2D::zero();
|
let container_size = Size2D::zero();
|
||||||
|
|
||||||
block_flow.base.stacking_relative_position =
|
block_flow.base.stacking_relative_position =
|
||||||
self.base.stacking_relative_position +
|
self.base.stacking_relative_position +
|
||||||
fragment.border_box.start.to_physical(self.base.writing_mode,
|
fragment.border_box.start.to_physical(self.base.writing_mode,
|
||||||
|
@ -1152,6 +1153,8 @@ impl Flow for InlineFlow {
|
||||||
}
|
}
|
||||||
InlineAbsoluteHypotheticalFragment(ref mut info) => {
|
InlineAbsoluteHypotheticalFragment(ref mut info) => {
|
||||||
let block_flow = info.flow_ref.as_block();
|
let block_flow = info.flow_ref.as_block();
|
||||||
|
block_flow.base.absolute_position_info = self.base.absolute_position_info;
|
||||||
|
|
||||||
// FIXME(#2795): Get the real container size
|
// FIXME(#2795): Get the real container size
|
||||||
let container_size = Size2D::zero();
|
let container_size = Size2D::zero();
|
||||||
block_flow.base.stacking_relative_position =
|
block_flow.base.stacking_relative_position =
|
||||||
|
@ -1184,15 +1187,6 @@ impl Flow for InlineFlow {
|
||||||
fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
|
fn update_late_computed_block_position_if_necessary(&mut self, _: Au) {}
|
||||||
|
|
||||||
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
fn build_display_list(&mut self, layout_context: &LayoutContext) {
|
||||||
let size = self.base.position.size.to_physical(self.base.writing_mode);
|
|
||||||
if !Rect(self.base.stacking_relative_position, size).intersects(&layout_context.shared
|
|
||||||
.dirty) {
|
|
||||||
debug!("inline block (stacking relative pos {}, size {}) didn't intersect dirty rect",
|
|
||||||
self.base.stacking_relative_position,
|
|
||||||
size);
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(#228): Once we form lines and have their cached bounds, we can be smarter and
|
// TODO(#228): Once we form lines and have their cached bounds, we can be smarter and
|
||||||
// not recurse on a line if nothing in it can intersect the dirty region.
|
// not recurse on a line if nothing in it can intersect the dirty region.
|
||||||
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
|
debug!("Flow: building display list for {:u} inline fragments", self.fragments.len());
|
||||||
|
@ -1211,6 +1205,11 @@ impl Flow for InlineFlow {
|
||||||
flow::mut_base(block_flow).display_list_building_result
|
flow::mut_base(block_flow).display_list_building_result
|
||||||
.add_to(&mut *display_list)
|
.add_to(&mut *display_list)
|
||||||
}
|
}
|
||||||
|
InlineAbsoluteHypotheticalFragment(ref mut block_flow) => {
|
||||||
|
let block_flow = block_flow.flow_ref.deref_mut();
|
||||||
|
flow::mut_base(block_flow).display_list_building_result
|
||||||
|
.add_to(&mut *display_list)
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,27 +25,27 @@ use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use geom::size::Size2D;
|
use geom::size::Size2D;
|
||||||
use geom::scale_factor::ScaleFactor;
|
use geom::scale_factor::ScaleFactor;
|
||||||
|
use gfx::color;
|
||||||
use gfx::display_list::{DisplayList, OpaqueNode, StackingContext};
|
use gfx::display_list::{DisplayList, OpaqueNode, StackingContext};
|
||||||
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
|
use gfx::font_cache_task::FontCacheTask;
|
||||||
use gfx::{render_task, color};
|
use gfx::render_task::{mod, RenderInitMsg, RenderChan, RenderLayer};
|
||||||
use layout_traits;
|
use layout_traits;
|
||||||
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
|
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
|
||||||
use log;
|
use log;
|
||||||
use script::dom::bindings::js::JS;
|
use script::dom::bindings::js::JS;
|
||||||
use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node};
|
use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node};
|
||||||
use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
|
use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
|
||||||
use script::layout_interface::{
|
use script::layout_interface::{AddStylesheetMsg, ContentBoxResponse, ContentBoxesResponse};
|
||||||
AddStylesheetMsg, ContentBoxResponse, ContentBoxesResponse, ContentBoxesQuery,
|
use script::layout_interface::{ContentBoxesQuery, ContentBoxQuery, ExitNowMsg, GetRPCMsg};
|
||||||
ContentBoxQuery, ExitNowMsg, GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC,
|
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, LoadStylesheetMsg};
|
||||||
LoadStylesheetMsg, MouseOverResponse, Msg, NoQuery, PrepareToExitMsg, ReapLayoutDataMsg,
|
use script::layout_interface::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg};
|
||||||
Reflow, ReflowForDisplay, ReflowMsg, ScriptLayoutChan, TrustedNodeAddress,
|
use script::layout_interface::{ReapLayoutDataMsg, Reflow, ReflowForDisplay, ReflowMsg};
|
||||||
};
|
use script::layout_interface::{ScriptLayoutChan, TrustedNodeAddress};
|
||||||
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
|
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
|
||||||
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
|
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
|
||||||
use servo_msg::compositor_msg::Scrollable;
|
use servo_msg::compositor_msg::Scrollable;
|
||||||
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
|
||||||
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
|
||||||
use gfx::font_cache_task::{FontCacheTask};
|
|
||||||
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
|
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
|
||||||
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
|
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
@ -679,6 +679,7 @@ impl LayoutTask {
|
||||||
let stacking_context = Arc::new(StackingContext::new(display_list,
|
let stacking_context = Arc::new(StackingContext::new(display_list,
|
||||||
origin,
|
origin,
|
||||||
0,
|
0,
|
||||||
|
1.0,
|
||||||
Some(render_layer)));
|
Some(render_layer)));
|
||||||
|
|
||||||
rw_data.stacking_context = Some(stacking_context.clone());
|
rw_data.stacking_context = Some(stacking_context.clone());
|
||||||
|
|
4
components/servo/Cargo.lock
generated
4
components/servo/Cargo.lock
generated
|
@ -27,7 +27,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azure"
|
name = "azure"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/rust-azure#64853170e435d9320f3fd828b9e7d16bc4c260a3"
|
source = "git+https://github.com/servo/rust-azure#d323c3c7c248d3d5a2d46a6a5ee61c6e92aec0b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
||||||
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
|
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
|
||||||
|
@ -212,7 +212,7 @@ source = "git+https://github.com/servo/libfreetype2#f5c49c0da1d5bc6b206c41763440
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "geom"
|
name = "geom"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/rust-geom#e5e74911ac6d3201009879b72499d6c681302611"
|
source = "git+https://github.com/servo/rust-geom#95e746133b4a35b53eb259304668b63ee8de42b8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gfx"
|
name = "gfx"
|
||||||
|
|
|
@ -1208,6 +1208,37 @@ pub mod longhands {
|
||||||
${switch_to_style_struct("Box")}
|
${switch_to_style_struct("Box")}
|
||||||
|
|
||||||
${single_keyword("box-sizing", "content-box border-box")}
|
${single_keyword("box-sizing", "content-box border-box")}
|
||||||
|
|
||||||
|
${new_style_struct("Effects", is_inherited=False)}
|
||||||
|
|
||||||
|
<%self:single_component_value name="opacity">
|
||||||
|
pub type SpecifiedValue = CSSFloat;
|
||||||
|
pub mod computed_value {
|
||||||
|
use super::super::CSSFloat;
|
||||||
|
pub type T = CSSFloat;
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn get_initial_value() -> computed_value::T {
|
||||||
|
1.0
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn to_computed_value(value: SpecifiedValue, _: &computed::Context)
|
||||||
|
-> computed_value::T {
|
||||||
|
if value < 0.0 {
|
||||||
|
0.0
|
||||||
|
} else if value > 1.0 {
|
||||||
|
1.0
|
||||||
|
} else {
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn from_component_value(input: &ComponentValue, _: &Url) -> Result<SpecifiedValue,()> {
|
||||||
|
match *input {
|
||||||
|
Number(ref value) => Ok(value.value),
|
||||||
|
_ => Err(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</%self:single_component_value>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
2
ports/android/glut_app/Cargo.lock
generated
2
ports/android/glut_app/Cargo.lock
generated
|
@ -15,7 +15,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azure"
|
name = "azure"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/rust-azure#612ffc4fbf80c1bd5faae4b86dfc539dda06fb0c"
|
source = "git+https://github.com/servo/rust-azure#d323c3c7c248d3d5a2d46a6a5ee61c6e92aec0b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
||||||
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
|
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
|
||||||
|
|
2
ports/cef/Cargo.lock
generated
2
ports/cef/Cargo.lock
generated
|
@ -26,7 +26,7 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "azure"
|
name = "azure"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/servo/rust-azure#612ffc4fbf80c1bd5faae4b86dfc539dda06fb0c"
|
source = "git+https://github.com/servo/rust-azure#d323c3c7c248d3d5a2d46a6a5ee61c6e92aec0b0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
"core_foundation 0.1.0 (git+https://github.com/servo/rust-core-foundation)",
|
||||||
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
|
"core_graphics 0.1.0 (git+https://github.com/servo/rust-core-graphics)",
|
||||||
|
|
|
@ -185,4 +185,5 @@ fragment=top != ../html/acid2.html acid2_ref.html
|
||||||
!= linear_gradients_corners_a.html linear_gradients_corners_ref.html
|
!= linear_gradients_corners_a.html linear_gradients_corners_ref.html
|
||||||
== linear_gradients_lengths_a.html linear_gradients_lengths_ref.html
|
== linear_gradients_lengths_a.html linear_gradients_lengths_ref.html
|
||||||
== incremental_float_a.html incremental_float_ref.html
|
== incremental_float_a.html incremental_float_ref.html
|
||||||
== table_specified_width_a.html table_specified_width_ref.html
|
== opacity_simple_a.html opacity_simple_ref.html
|
||||||
|
== opacity_stacking_context_a.html opacity_stacking_context_ref.html
|
||||||
|
|
27
tests/ref/opacity_simple_a.html
Normal file
27
tests/ref/opacity_simple_a.html
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that opacity works. -->
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
position: absolute;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
top: 50px;
|
||||||
|
left: 50px;
|
||||||
|
}
|
||||||
|
#a {
|
||||||
|
background: #800000;
|
||||||
|
}
|
||||||
|
#b {
|
||||||
|
background: #000080;
|
||||||
|
opacity: 0.75;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section id=a></section>
|
||||||
|
<section id=b></section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
20
tests/ref/opacity_simple_ref.html
Normal file
20
tests/ref/opacity_simple_ref.html
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that opacity works. -->
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
position: absolute;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
top: 50px;
|
||||||
|
left: 50px;
|
||||||
|
background: #200060;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section></section>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
48
tests/ref/opacity_stacking_context_a.html
Normal file
48
tests/ref/opacity_stacking_context_a.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that `opacity` causes a new stacking context to be formed. -->
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
position: absolute;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#a {
|
||||||
|
background: red;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#b {
|
||||||
|
background: #00ff00;
|
||||||
|
top: 25px;
|
||||||
|
left: 25px;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c {
|
||||||
|
background: blue;
|
||||||
|
top: 50px;
|
||||||
|
left: 50px;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
opacity: 0.5;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section id=a></section>
|
||||||
|
<section id=c></section>
|
||||||
|
<div id=container>
|
||||||
|
<section id=b></section>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
48
tests/ref/opacity_stacking_context_ref.html
Normal file
48
tests/ref/opacity_stacking_context_ref.html
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<!-- Tests that `opacity` causes a new stacking context to be formed. -->
|
||||||
|
<style>
|
||||||
|
section {
|
||||||
|
position: absolute;
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#a {
|
||||||
|
background: red;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#b {
|
||||||
|
background: #00ff00;
|
||||||
|
top: 25px;
|
||||||
|
left: 25px;
|
||||||
|
z-index: 1;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
#c {
|
||||||
|
background: blue;
|
||||||
|
top: 50px;
|
||||||
|
left: 50px;
|
||||||
|
z-index: 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
#container {
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<section id=a></section>
|
||||||
|
<section id=c></section>
|
||||||
|
<div id=container>
|
||||||
|
<section id=b></section>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue