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:
Patrick Walton 2014-11-18 15:37:53 -08:00
parent 873ca6cadd
commit 1c1c507c03
17 changed files with 421 additions and 146 deletions

View file

@ -1722,20 +1722,31 @@ impl Flow for BlockFlow {
}
// 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 {
stacking_relative_position_of_absolute_containing_block:
if self.fragment.establishes_stacking_context() {
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
},
stacking_relative_position_of_absolute_containing_block_for_children,
relative_containing_block_size: self.fragment.content_box().size,
layers_needed_for_positioned_flows: self.base
.flags
@ -1760,17 +1771,11 @@ impl Flow for BlockFlow {
origin_for_children +
(kid_base.position.start + relative_offset).to_physical(writing_mode,
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
}
// 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) {

View file

@ -12,8 +12,7 @@
use block::BlockFlow;
use context::LayoutContext;
use flow::{mod, Flow};
use flow::{IS_ABSOLUTELY_POSITIONED, NEEDS_LAYER};
use flow::{mod, Flow, NEEDS_LAYER};
use fragment::{Fragment, GenericFragment, IframeFragment, IframeFragmentInfo, ImageFragment};
use fragment::{ImageFragmentInfo, InlineAbsoluteHypotheticalFragment, InlineBlockFragment};
use fragment::{ScannedTextFragment, ScannedTextFragmentInfo, TableFragment};
@ -811,6 +810,10 @@ pub trait BlockFlowDisplayListBuilding {
fn build_display_list_for_absolutely_positioned_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 {
@ -828,19 +831,8 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
&self.base.clip_rect);
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);
}
// 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,
@ -850,7 +842,12 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
self.build_display_list_for_block_base(&mut *display_list,
layout_context,
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,
@ -860,18 +857,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
layout_context,
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 &&
!self.base.flags.contains(NEEDS_LAYER) {
// We didn't need a layer.
self.base.display_list_building_result =
StackingContextResult(Arc::new(StackingContext::new(display_list,
bounds,
z_index,
None)));
StackingContextResult(self.create_stacking_context(display_list, None));
return
}
@ -884,13 +874,10 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
let transparent = color::rgba(1.0, 1.0, 1.0, 0.0);
let stacking_context =
Arc::new(StackingContext::new(display_list,
bounds,
z_index,
Some(Arc::new(RenderLayer::new(self.layer_id(0),
transparent,
scroll_policy)))));
self.create_stacking_context(display_list,
Some(Arc::new(RenderLayer::new(self.layer_id(0),
transparent,
scroll_policy))));
self.base.display_list_building_result = StackingContextResult(stacking_context)
}
@ -900,7 +887,23 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
layout_context,
RootOfStackingContextLevel);
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))
}
}

View file

@ -1494,6 +1494,9 @@ impl Fragment {
/// Returns true if this fragment establishes a new stacking context and false otherwise.
pub fn establishes_stacking_context(&self) -> bool {
if self.style().get_effects().opacity != 1.0 {
return true
}
match self.style().get_box().position {
position::absolute | position::fixed => {
// FIXME(pcwalton): This should only establish a new stacking context when

View file

@ -20,7 +20,7 @@ use model::IntrinsicISizesContribution;
use text;
use collections::{RingBuf};
use geom::{Rect, Size2D};
use geom::Size2D;
use gfx::display_list::DisplayList;
use gfx::font::FontMetrics;
use gfx::font_context::FontContext;
@ -1141,9 +1141,10 @@ impl Flow for InlineFlow {
let stacking_relative_position = match fragment.specific {
InlineBlockFragment(ref mut info) => {
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
let container_size = Size2D::zero();
block_flow.base.stacking_relative_position =
self.base.stacking_relative_position +
fragment.border_box.start.to_physical(self.base.writing_mode,
@ -1152,6 +1153,8 @@ impl Flow for InlineFlow {
}
InlineAbsoluteHypotheticalFragment(ref mut info) => {
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
let container_size = Size2D::zero();
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 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
// 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());
@ -1211,6 +1205,11 @@ impl Flow for InlineFlow {
flow::mut_base(block_flow).display_list_building_result
.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)
}
_ => {}
}
}

View file

@ -25,27 +25,27 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::scale_factor::ScaleFactor;
use gfx::color;
use gfx::display_list::{DisplayList, OpaqueNode, StackingContext};
use gfx::render_task::{RenderInitMsg, RenderChan, RenderLayer};
use gfx::{render_task, color};
use gfx::font_cache_task::FontCacheTask;
use gfx::render_task::{mod, RenderInitMsg, RenderChan, RenderLayer};
use layout_traits;
use layout_traits::{LayoutControlMsg, LayoutTaskFactory};
use log;
use script::dom::bindings::js::JS;
use script::dom::node::{ElementNodeTypeId, LayoutDataRef, Node};
use script::dom::element::{HTMLBodyElementTypeId, HTMLHtmlElementTypeId};
use script::layout_interface::{
AddStylesheetMsg, ContentBoxResponse, ContentBoxesResponse, ContentBoxesQuery,
ContentBoxQuery, ExitNowMsg, GetRPCMsg, HitTestResponse, LayoutChan, LayoutRPC,
LoadStylesheetMsg, MouseOverResponse, Msg, NoQuery, PrepareToExitMsg, ReapLayoutDataMsg,
Reflow, ReflowForDisplay, ReflowMsg, ScriptLayoutChan, TrustedNodeAddress,
};
use script::layout_interface::{AddStylesheetMsg, ContentBoxResponse, ContentBoxesResponse};
use script::layout_interface::{ContentBoxesQuery, ContentBoxQuery, ExitNowMsg, GetRPCMsg};
use script::layout_interface::{HitTestResponse, LayoutChan, LayoutRPC, LoadStylesheetMsg};
use script::layout_interface::{MouseOverResponse, Msg, NoQuery, PrepareToExitMsg};
use script::layout_interface::{ReapLayoutDataMsg, Reflow, ReflowForDisplay, ReflowMsg};
use script::layout_interface::{ScriptLayoutChan, TrustedNodeAddress};
use script_traits::{SendEventMsg, ReflowEvent, ReflowCompleteMsg, OpaqueScriptLayoutChannel};
use script_traits::{ScriptControlChan, UntrustedNodeAddress};
use servo_msg::compositor_msg::Scrollable;
use servo_msg::constellation_msg::{ConstellationChan, PipelineId, Failure, FailureMsg};
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::resource_task::{ResourceTask, load_bytes_iter};
use servo_util::geometry::Au;
@ -679,6 +679,7 @@ impl LayoutTask {
let stacking_context = Arc::new(StackingContext::new(display_list,
origin,
0,
1.0,
Some(render_layer)));
rw_data.stacking_context = Some(stacking_context.clone());