mirror of
https://github.com/servo/servo.git
synced 2025-07-16 20:03:39 +01:00
Auto merge of #8140 - mrobinson:canvas, r=pcwalton
Integrate Canvas into the DisplayList Canvas is currently given a layer at the stacking context level. Instead it's DisplayItem should be given a layer directly. This fixes painting order issues where canvases are painted on top of other positioned content that is later in tree order. It always simplifies the code a bit. <!-- Reviewable:start --> [<img src="https://reviewable.io/review_button.png" height=40 alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/8140) <!-- Reviewable:end -->
This commit is contained in:
commit
22a6884a67
5 changed files with 83 additions and 74 deletions
|
@ -245,6 +245,7 @@ pub trait FragmentDisplayListBuilding {
|
||||||
/// A helper method that `build_display_list` calls to create per-fragment-type display items.
|
/// A helper method that `build_display_list` calls to create per-fragment-type display items.
|
||||||
fn build_fragment_type_specific_display_items(&mut self,
|
fn build_fragment_type_specific_display_items(&mut self,
|
||||||
display_list: &mut DisplayList,
|
display_list: &mut DisplayList,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
stacking_relative_border_box: &Rect<Au>,
|
stacking_relative_border_box: &Rect<Au>,
|
||||||
clip: &ClippingRegion);
|
clip: &ClippingRegion);
|
||||||
|
|
||||||
|
@ -252,7 +253,6 @@ pub trait FragmentDisplayListBuilding {
|
||||||
fn create_stacking_context(&self,
|
fn create_stacking_context(&self,
|
||||||
base_flow: &BaseFlow,
|
base_flow: &BaseFlow,
|
||||||
display_list: Box<DisplayList>,
|
display_list: Box<DisplayList>,
|
||||||
layout_context: &LayoutContext,
|
|
||||||
scroll_policy: ScrollPolicy,
|
scroll_policy: ScrollPolicy,
|
||||||
mode: StackingContextCreationMode)
|
mode: StackingContextCreationMode)
|
||||||
-> Arc<StackingContext>;
|
-> Arc<StackingContext>;
|
||||||
|
@ -1042,6 +1042,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
|
|
||||||
// Create special per-fragment-type display items.
|
// Create special per-fragment-type display items.
|
||||||
self.build_fragment_type_specific_display_items(display_list,
|
self.build_fragment_type_specific_display_items(display_list,
|
||||||
|
layout_context,
|
||||||
&stacking_relative_border_box,
|
&stacking_relative_border_box,
|
||||||
&clip);
|
&clip);
|
||||||
|
|
||||||
|
@ -1054,6 +1055,7 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
|
|
||||||
fn build_fragment_type_specific_display_items(&mut self,
|
fn build_fragment_type_specific_display_items(&mut self,
|
||||||
display_list: &mut DisplayList,
|
display_list: &mut DisplayList,
|
||||||
|
layout_context: &LayoutContext,
|
||||||
stacking_relative_border_box: &Rect<Au>,
|
stacking_relative_border_box: &Rect<Au>,
|
||||||
clip: &ClippingRegion) {
|
clip: &ClippingRegion) {
|
||||||
// Compute the context box position relative to the parent stacking context.
|
// Compute the context box position relative to the parent stacking context.
|
||||||
|
@ -1154,16 +1156,25 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
.computed_inline_size.map_or(0, |w| w.to_px() as usize);
|
.computed_inline_size.map_or(0, |w| w.to_px() as usize);
|
||||||
let height = canvas_fragment_info.replaced_image_fragment_info
|
let height = canvas_fragment_info.replaced_image_fragment_info
|
||||||
.computed_block_size.map_or(0, |h| h.to_px() as usize);
|
.computed_block_size.map_or(0, |h| h.to_px() as usize);
|
||||||
|
if width > 0 && height > 0 {
|
||||||
let (sender, receiver) = ipc::channel::<IpcSharedMemory>().unwrap();
|
let (sender, receiver) = ipc::channel::<IpcSharedMemory>().unwrap();
|
||||||
|
let layer_id = self.layer_id();
|
||||||
let canvas_data = match canvas_fragment_info.ipc_renderer {
|
let canvas_data = match canvas_fragment_info.ipc_renderer {
|
||||||
Some(ref ipc_renderer) => {
|
Some(ref ipc_renderer) => {
|
||||||
ipc_renderer.lock().unwrap().send(CanvasMsg::FromLayout(
|
let ipc_renderer = ipc_renderer.lock().unwrap();
|
||||||
|
ipc_renderer.send(CanvasMsg::FromLayout(
|
||||||
FromLayoutMsg::SendPixelContents(sender))).unwrap();
|
FromLayoutMsg::SendPixelContents(sender))).unwrap();
|
||||||
receiver.recv().unwrap()
|
let data = receiver.recv().unwrap();
|
||||||
|
|
||||||
|
// Propagate the layer and the renderer to the paint task.
|
||||||
|
layout_context.shared.canvas_layers_sender.send(
|
||||||
|
(layer_id, (*ipc_renderer).clone())).unwrap();
|
||||||
|
|
||||||
|
data
|
||||||
},
|
},
|
||||||
None => IpcSharedMemory::from_byte(0xFFu8, width * height * 4),
|
None => IpcSharedMemory::from_byte(0xFFu8, width * height * 4),
|
||||||
};
|
};
|
||||||
display_list.content.push_back(DisplayItem::ImageClass(box ImageDisplayItem {
|
let display_item = DisplayItem::ImageClass(box ImageDisplayItem {
|
||||||
base: BaseDisplayItem::new(stacking_relative_content_box,
|
base: BaseDisplayItem::new(stacking_relative_content_box,
|
||||||
DisplayItemMetadata::new(self.node,
|
DisplayItemMetadata::new(self.node,
|
||||||
&*self.style,
|
&*self.style,
|
||||||
|
@ -1177,7 +1188,16 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
}),
|
}),
|
||||||
stretch_size: stacking_relative_content_box.size,
|
stretch_size: stacking_relative_content_box.size,
|
||||||
image_rendering: image_rendering::T::Auto,
|
image_rendering: image_rendering::T::Auto,
|
||||||
|
});
|
||||||
|
|
||||||
|
display_list.content.push_back(DisplayItem::LayeredItemClass(box LayeredItem {
|
||||||
|
item: display_item,
|
||||||
|
layer_id: layer_id
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
display_list.layer_info.push_back(
|
||||||
|
LayerInfo::new(layer_id, ScrollPolicy::Scrollable, None));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
SpecificFragmentInfo::UnscannedText(_) => {
|
SpecificFragmentInfo::UnscannedText(_) => {
|
||||||
panic!("Shouldn't see unscanned fragments here.")
|
panic!("Shouldn't see unscanned fragments here.")
|
||||||
|
@ -1191,7 +1211,6 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
fn create_stacking_context(&self,
|
fn create_stacking_context(&self,
|
||||||
base_flow: &BaseFlow,
|
base_flow: &BaseFlow,
|
||||||
display_list: Box<DisplayList>,
|
display_list: Box<DisplayList>,
|
||||||
layout_context: &LayoutContext,
|
|
||||||
scroll_policy: ScrollPolicy,
|
scroll_policy: ScrollPolicy,
|
||||||
mode: StackingContextCreationMode)
|
mode: StackingContextCreationMode)
|
||||||
-> Arc<StackingContext> {
|
-> Arc<StackingContext> {
|
||||||
|
@ -1305,32 +1324,16 @@ impl FragmentDisplayListBuilding for Fragment {
|
||||||
filters.push(Filter::Opacity(effects.opacity))
|
filters.push(Filter::Opacity(effects.opacity))
|
||||||
}
|
}
|
||||||
|
|
||||||
let canvas = match self.specific {
|
// There are two situations that need layers: when the fragment has the HAS_LAYER
|
||||||
SpecificFragmentInfo::Canvas(_) => true,
|
// flag and when we are building a layer tree for overflow scrolling.
|
||||||
_ => false
|
|
||||||
};
|
|
||||||
|
|
||||||
// There are three situations that need layers: when the fragment has the HAS_LAYER
|
|
||||||
// flag, when this is a canvas or iframe fragment, and when we are building a layer
|
|
||||||
// tree for overflow scrolling.
|
|
||||||
let layer_info = if mode == StackingContextCreationMode::InnerScrollWrapper {
|
let layer_info = if mode == StackingContextCreationMode::InnerScrollWrapper {
|
||||||
Some(LayerInfo::new(self.layer_id_for_overflow_scroll(), scroll_policy, None))
|
Some(LayerInfo::new(self.layer_id_for_overflow_scroll(), scroll_policy, None))
|
||||||
} else if self.flags.contains(HAS_LAYER) || canvas {
|
} else if self.flags.contains(HAS_LAYER) {
|
||||||
Some(LayerInfo::new(self.layer_id(), scroll_policy, None))
|
Some(LayerInfo::new(self.layer_id(), scroll_policy, None))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
// If it's a canvas we must propagate the layer and the renderer to the paint task.
|
|
||||||
if let SpecificFragmentInfo::Canvas(ref fragment_info) = self.specific {
|
|
||||||
let layer_id = layer_info.unwrap().layer_id;
|
|
||||||
if let Some(ref ipc_renderer) = fragment_info.ipc_renderer {
|
|
||||||
layout_context.shared
|
|
||||||
.canvas_layers_sender
|
|
||||||
.send((layer_id, (*ipc_renderer.lock().unwrap()).clone())).unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let scrolls_overflow_area = mode == StackingContextCreationMode::OuterScrollWrapper;
|
let scrolls_overflow_area = mode == StackingContextCreationMode::OuterScrollWrapper;
|
||||||
let transform_style = self.style().get_used_transform_style();
|
let transform_style = self.style().get_used_transform_style();
|
||||||
let establishes_3d_context = scrolls_overflow_area ||
|
let establishes_3d_context = scrolls_overflow_area ||
|
||||||
|
@ -1603,7 +1606,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
self.fragment.create_stacking_context(
|
self.fragment.create_stacking_context(
|
||||||
&self.base,
|
&self.base,
|
||||||
display_list,
|
display_list,
|
||||||
layout_context,
|
|
||||||
scroll_policy,
|
scroll_policy,
|
||||||
StackingContextCreationMode::Normal))
|
StackingContextCreationMode::Normal))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1685,13 +1687,11 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
outer_display_list.children.push_back(self.fragment.create_stacking_context(
|
outer_display_list.children.push_back(self.fragment.create_stacking_context(
|
||||||
&self.base,
|
&self.base,
|
||||||
display_list,
|
display_list,
|
||||||
layout_context,
|
|
||||||
scroll_policy,
|
scroll_policy,
|
||||||
StackingContextCreationMode::InnerScrollWrapper));
|
StackingContextCreationMode::InnerScrollWrapper));
|
||||||
self.fragment.create_stacking_context(
|
self.fragment.create_stacking_context(
|
||||||
&self.base,
|
&self.base,
|
||||||
outer_display_list,
|
outer_display_list,
|
||||||
layout_context,
|
|
||||||
scroll_policy,
|
scroll_policy,
|
||||||
StackingContextCreationMode::OuterScrollWrapper)
|
StackingContextCreationMode::OuterScrollWrapper)
|
||||||
}
|
}
|
||||||
|
@ -1699,7 +1699,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
self.fragment.create_stacking_context(
|
self.fragment.create_stacking_context(
|
||||||
&self.base,
|
&self.base,
|
||||||
display_list,
|
display_list,
|
||||||
layout_context,
|
|
||||||
scroll_policy,
|
scroll_policy,
|
||||||
StackingContextCreationMode::Normal)
|
StackingContextCreationMode::Normal)
|
||||||
}
|
}
|
||||||
|
@ -1723,7 +1722,6 @@ impl BlockFlowDisplayListBuilding for BlockFlow {
|
||||||
DisplayListBuildingResult::StackingContext(
|
DisplayListBuildingResult::StackingContext(
|
||||||
self.fragment.create_stacking_context(&self.base,
|
self.fragment.create_stacking_context(&self.base,
|
||||||
display_list,
|
display_list,
|
||||||
layout_context,
|
|
||||||
ScrollPolicy::Scrollable,
|
ScrollPolicy::Scrollable,
|
||||||
StackingContextCreationMode::Normal))
|
StackingContextCreationMode::Normal))
|
||||||
} else {
|
} else {
|
||||||
|
@ -1830,7 +1828,6 @@ impl InlineFlowDisplayListBuilding for InlineFlow {
|
||||||
self.fragments.fragments[0].create_stacking_context(
|
self.fragments.fragments[0].create_stacking_context(
|
||||||
&self.base,
|
&self.base,
|
||||||
display_list,
|
display_list,
|
||||||
layout_context,
|
|
||||||
ScrollPolicy::Scrollable,
|
ScrollPolicy::Scrollable,
|
||||||
StackingContextCreationMode::Normal))
|
StackingContextCreationMode::Normal))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -2106,16 +2106,6 @@ impl Fragment {
|
||||||
stacking_relative_border_box.size.height - border_padding.vertical()))
|
stacking_relative_border_box.size.height - border_padding.vertical()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns true if this fragment unconditionally layerizes.
|
|
||||||
pub fn needs_layered_stacking_context(&self) -> bool {
|
|
||||||
// Canvas and iframes always layerize, as an special case
|
|
||||||
// FIXME(pcwalton): Don't unconditionally form stacking contexts for each canvas.
|
|
||||||
match self.specific {
|
|
||||||
SpecificFragmentInfo::Canvas(_) => true,
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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.flags.contains(HAS_LAYER) {
|
if self.flags.contains(HAS_LAYER) {
|
||||||
|
@ -2140,10 +2130,6 @@ impl Fragment {
|
||||||
transform_style::T::auto => {}
|
transform_style::T::auto => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.needs_layered_stacking_context() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME(pcwalton): Don't unconditionally form stacking contexts for `overflow_x: scroll`
|
// FIXME(pcwalton): Don't unconditionally form stacking contexts for `overflow_x: scroll`
|
||||||
// and `overflow_y: scroll`. This needs multiple layers per stacking context.
|
// and `overflow_y: scroll`. This needs multiple layers per stacking context.
|
||||||
match (self.style().get_box().position,
|
match (self.style().get_box().position,
|
||||||
|
|
|
@ -10,7 +10,7 @@ use context::LayoutContext;
|
||||||
use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding};
|
use display_list_builder::{FragmentDisplayListBuilding, InlineFlowDisplayListBuilding};
|
||||||
use euclid::{Point2D, Rect, Size2D};
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use floats::{FloatKind, Floats, PlacementInfo};
|
use floats::{FloatKind, Floats, PlacementInfo};
|
||||||
use flow::{EarlyAbsolutePositionInfo, LAYERS_NEEDED_FOR_DESCENDANTS, MutableFlowUtils, OpaqueFlow};
|
use flow::{EarlyAbsolutePositionInfo, MutableFlowUtils, OpaqueFlow};
|
||||||
use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED};
|
use flow::{self, BaseFlow, Flow, FlowClass, ForceNonfloatedFlag, IS_ABSOLUTELY_POSITIONED};
|
||||||
use flow_ref;
|
use flow_ref;
|
||||||
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
|
use fragment::{CoordinateSystem, Fragment, FragmentBorderBoxIterator, SpecificFragmentInfo};
|
||||||
|
@ -1467,7 +1467,6 @@ impl Flow for InlineFlow {
|
||||||
|
|
||||||
// Now, go through each line and lay out the fragments inside.
|
// Now, go through each line and lay out the fragments inside.
|
||||||
let mut line_distance_from_flow_block_start = Au(0);
|
let mut line_distance_from_flow_block_start = Au(0);
|
||||||
let mut layers_needed_for_descendants = false;
|
|
||||||
let line_count = self.lines.len();
|
let line_count = self.lines.len();
|
||||||
for line_index in 0..line_count {
|
for line_index in 0..line_count {
|
||||||
let line = &mut self.lines[line_index];
|
let line = &mut self.lines[line_index];
|
||||||
|
@ -1496,10 +1495,6 @@ impl Flow for InlineFlow {
|
||||||
for fragment_index in line.range.each_index() {
|
for fragment_index in line.range.each_index() {
|
||||||
let fragment = &mut self.fragments.fragments[fragment_index.to_usize()];
|
let fragment = &mut self.fragments.fragments[fragment_index.to_usize()];
|
||||||
|
|
||||||
if fragment.needs_layered_stacking_context() && !fragment.is_positioned() {
|
|
||||||
layers_needed_for_descendants = true
|
|
||||||
}
|
|
||||||
|
|
||||||
let InlineMetrics {
|
let InlineMetrics {
|
||||||
mut block_size_above_baseline,
|
mut block_size_above_baseline,
|
||||||
mut depth_below_baseline,
|
mut depth_below_baseline,
|
||||||
|
@ -1596,10 +1591,6 @@ impl Flow for InlineFlow {
|
||||||
kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id);
|
kid.assign_block_size_for_inorder_child_if_necessary(layout_context, thread_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mark ourselves for layerization if that will be necessary to paint in the proper
|
|
||||||
// order (CSS 2.1, Appendix E).
|
|
||||||
self.base.flags.set(LAYERS_NEEDED_FOR_DESCENDANTS, layers_needed_for_descendants);
|
|
||||||
|
|
||||||
if self.contains_positioned_fragments() {
|
if self.contains_positioned_fragments() {
|
||||||
// Assign block-sizes for all flows in this absolute flow tree.
|
// Assign block-sizes for all flows in this absolute flow tree.
|
||||||
// This is preorder because the block-size of an absolute flow may depend on
|
// This is preorder because the block-size of an absolute flow may depend on
|
||||||
|
|
|
@ -43,5 +43,30 @@
|
||||||
<div class="gray box" style="position: relative; left: 20px; top: -30px;"> </div>
|
<div class="gray box" style="position: relative; left: 20px; top: -30px;"> </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- The canvas should be painted in tree order since it is not positioned and
|
||||||
|
the following div is. -->
|
||||||
|
<div class="test grayest box">
|
||||||
|
<canvas class="box" id="canvas1" style="display: block; padding-left: 10px; padding-top: 10px;" width="50" height="50"></canvas>
|
||||||
|
<div class="gray box" style="position: relative; left: 20px; top: -40px;"> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- The canvas should be painted in tree order since both it and the inner div are
|
||||||
|
not positioned elements. -->
|
||||||
|
<div class="test grayest box">
|
||||||
|
<canvas class="box" id="canvas2" style="display: block; position: relative; left: 10px; top: 10px;" width="50" height="50"></canvas>
|
||||||
|
<div class="gray box" style="position: relative; left: 20px; top: -30px;"> </div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
function paintCanvas(id) {
|
||||||
|
var canvas = document.getElementById(id);
|
||||||
|
var context = canvas.getContext("2d");
|
||||||
|
context.fillStyle = "rgb(80, 80, 80)";
|
||||||
|
context.fillRect(0, 0, 100, 100);
|
||||||
|
}
|
||||||
|
paintCanvas("canvas1");
|
||||||
|
paintCanvas("canvas2");
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -34,5 +34,15 @@
|
||||||
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
|
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
|
||||||
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
|
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="test grayest box">
|
||||||
|
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
|
||||||
|
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="test grayest box">
|
||||||
|
<div class="grayer box" style="margin-left: 10px; margin-top: 10px;"></div>
|
||||||
|
<div class="gray box" style="margin-left: 20px; margin-top: -40px;"></div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue