use std::cmp::Ordering;
use std::collections::HashMap;
use std::{f32, fmt};
use base::id::PipelineId;
use base::print_tree::PrintTree;
use embedder_traits::Cursor;
use euclid::{SideOffsets2D, Vector2D};
use pixels::Image;
use serde::Serialize;
use servo_geometry::MaxRect;
use style::computed_values::_servo_top_layer::T as InTopLayer;
pub use style::dom::OpaqueNode;
use webrender_api as wr;
use webrender_api::units::{LayoutPixel, LayoutRect, LayoutTransform};
use webrender_api::{
BorderRadius, ClipChainId, ClipMode, CommonItemProperties, ComplexClipRegion, ExternalScrollId,
FilterOp, GlyphInstance, GradientStop, ImageKey, MixBlendMode, PrimitiveFlags, Shadow,
SpatialId, StickyOffsetBounds, TransformStyle,
};
use webrender_traits::display_list::{ScrollSensitivity, ScrollTreeNodeId};
use super::StackingContextId;
pub static BLUR_INFLATION_FACTOR: i32 = 3;
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub struct ClipScrollNodeIndex(usize);
impl ClipScrollNodeIndex {
pub fn root_scroll_node() -> ClipScrollNodeIndex {
ClipScrollNodeIndex(1)
}
pub fn root_reference_frame() -> ClipScrollNodeIndex {
ClipScrollNodeIndex(0)
}
pub fn new(index: usize) -> ClipScrollNodeIndex {
assert_ne!(index, 0, "Use the root_reference_frame constructor");
assert_ne!(index, 1, "Use the root_scroll_node constructor");
ClipScrollNodeIndex(index)
}
pub fn is_root_scroll_node(&self) -> bool {
*self == Self::root_scroll_node()
}
pub fn to_define_item(&self) -> DisplayItem {
DisplayItem::DefineClipScrollNode(Box::new(DefineClipScrollNodeItem {
base: BaseDisplayItem::empty(),
node_index: *self,
}))
}
pub fn to_index(self) -> usize {
self.0
}
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub struct ClippingAndScrolling {
pub scrolling: ClipScrollNodeIndex,
pub clipping: Option<ClipScrollNodeIndex>,
}
impl ClippingAndScrolling {
pub fn simple(scrolling: ClipScrollNodeIndex) -> ClippingAndScrolling {
ClippingAndScrolling {
scrolling,
clipping: None,
}
}
pub fn new(scrolling: ClipScrollNodeIndex, clipping: ClipScrollNodeIndex) -> Self {
ClippingAndScrolling {
scrolling,
clipping: Some(clipping),
}
}
}
#[derive(Serialize)]
pub struct DisplayList {
pub list: Vec<DisplayItem>,
pub clip_scroll_nodes: Vec<ClipScrollNode>,
}
impl DisplayList {
pub fn bounds(&self) -> LayoutRect {
match self.list.first() {
Some(DisplayItem::PushStackingContext(item)) => item.stacking_context.bounds,
Some(_) => unreachable!("Root element of display list not stacking context."),
None => LayoutRect::zero(),
}
}
pub fn print(&self) {
let mut print_tree = PrintTree::new("Display List".to_owned());
self.print_with_tree(&mut print_tree);
}
pub fn print_with_tree(&self, print_tree: &mut PrintTree) {
print_tree.new_level("ClipScrollNodes".to_owned());
for node in &self.clip_scroll_nodes {
print_tree.add_item(format!("{:?}", node));
}
print_tree.end_level();
print_tree.new_level("Items".to_owned());
for item in &self.list {
print_tree.add_item(format!(
"{:?} StackingContext: {:?} {:?}",
item,
item.base().stacking_context_id,
item.clipping_and_scrolling()
));
}
print_tree.end_level();
}
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum DisplayListSection {
BackgroundAndBorders,
BlockBackgroundsAndBorders,
Content,
Outlines,
}
#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd, Serialize)]
pub enum StackingContextType {
Real,
PseudoPositioned,
PseudoFloat,
}
#[derive(Clone, Serialize)]
pub struct StackingContext {
pub id: StackingContextId,
pub context_type: StackingContextType,
pub bounds: LayoutRect,
pub overflow: LayoutRect,
pub z_index: i32,
pub in_top_layer: InTopLayer,
pub filters: Vec<FilterOp>,
pub mix_blend_mode: MixBlendMode,
pub transform: Option<LayoutTransform>,
pub transform_style: TransformStyle,
pub perspective: Option<LayoutTransform>,
pub parent_clipping_and_scrolling: ClippingAndScrolling,
pub established_reference_frame: Option<ClipScrollNodeIndex>,
}
impl StackingContext {
#[inline]
#[allow(clippy::too_many_arguments)]
pub fn new(
id: StackingContextId,
context_type: StackingContextType,
bounds: LayoutRect,
overflow: LayoutRect,
z_index: i32,
in_top_layer: InTopLayer,
filters: Vec<FilterOp>,
mix_blend_mode: MixBlendMode,
transform: Option<LayoutTransform>,
transform_style: TransformStyle,
perspective: Option<LayoutTransform>,
parent_clipping_and_scrolling: ClippingAndScrolling,
established_reference_frame: Option<ClipScrollNodeIndex>,
) -> StackingContext {
if let Some(ref t) = transform {
assert_ne!(t.m11, 0.);
assert_ne!(t.m22, 0.);
}
StackingContext {
id,
context_type,
bounds,
overflow,
z_index,
in_top_layer,
filters,
mix_blend_mode,
transform,
transform_style,
perspective,
parent_clipping_and_scrolling,
established_reference_frame,
}
}
#[inline]
pub fn root() -> StackingContext {
StackingContext::new(
StackingContextId::root(),
StackingContextType::Real,
LayoutRect::zero(),
LayoutRect::zero(),
0,
InTopLayer::None,
vec![],
MixBlendMode::Normal,
None,
TransformStyle::Flat,
None,
ClippingAndScrolling::simple(ClipScrollNodeIndex::root_scroll_node()),
None,
)
}
pub fn to_display_list_items(self) -> (DisplayItem, DisplayItem) {
let mut base_item = BaseDisplayItem::empty();
base_item.stacking_context_id = self.id;
base_item.clipping_and_scrolling = self.parent_clipping_and_scrolling;
let pop_item = DisplayItem::PopStackingContext(Box::new(PopStackingContextItem {
base: base_item.clone(),
stacking_context_id: self.id,
established_reference_frame: self.established_reference_frame.is_some(),
}));
let push_item = DisplayItem::PushStackingContext(Box::new(PushStackingContextItem {
base: base_item,
stacking_context: self,
}));
(push_item, pop_item)
}
}
impl Ord for StackingContext {
fn cmp(&self, other: &Self) -> Ordering {
if self.in_top_layer == InTopLayer::Top {
if other.in_top_layer == InTopLayer::Top {
return Ordering::Equal;
} else {
return Ordering::Greater;
}
} else if other.in_top_layer == InTopLayer::Top {
return Ordering::Less;
}
if self.z_index != 0 || other.z_index != 0 {
return self.z_index.cmp(&other.z_index);
}
match (self.context_type, other.context_type) {
(StackingContextType::PseudoFloat, StackingContextType::PseudoFloat) => Ordering::Equal,
(StackingContextType::PseudoFloat, _) => Ordering::Less,
(_, StackingContextType::PseudoFloat) => Ordering::Greater,
(_, _) => Ordering::Equal,
}
}
}
impl PartialOrd for StackingContext {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for StackingContext {}
impl PartialEq for StackingContext {
fn eq(&self, other: &Self) -> bool {
self.id == other.id
}
}
impl fmt::Debug for StackingContext {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let type_string = if self.context_type == StackingContextType::Real {
"StackingContext"
} else {
"Pseudo-StackingContext"
};
write!(
f,
"{} at {:?} with overflow {:?}: {:?}",
type_string, self.bounds, self.overflow, self.id
)
}
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub struct StickyFrameData {
pub margins: SideOffsets2D<Option<f32>, LayoutPixel>,
pub vertical_offset_bounds: StickyOffsetBounds,
pub horizontal_offset_bounds: StickyOffsetBounds,
}
#[derive(Clone, Copy, Debug, PartialEq, Serialize)]
pub enum ClipType {
Rounded(ComplexClipRegion),
Rect,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
pub enum ClipScrollNodeType {
Placeholder,
ScrollFrame(ScrollSensitivity, ExternalScrollId),
StickyFrame(StickyFrameData),
Clip(ClipType),
}
#[derive(Clone, Debug, Serialize)]
pub struct ClipScrollNode {
pub parent_index: ClipScrollNodeIndex,
pub clip: ClippingRegion,
pub content_rect: LayoutRect,
pub node_type: ClipScrollNodeType,
pub scroll_node_id: Option<ScrollTreeNodeId>,
pub clip_chain_id: Option<ClipChainId>,
}
impl ClipScrollNode {
pub fn placeholder() -> ClipScrollNode {
ClipScrollNode {
parent_index: ClipScrollNodeIndex(0),
clip: ClippingRegion::from_rect(LayoutRect::zero()),
content_rect: LayoutRect::zero(),
node_type: ClipScrollNodeType::Placeholder,
scroll_node_id: None,
clip_chain_id: None,
}
}
pub fn is_placeholder(&self) -> bool {
self.node_type == ClipScrollNodeType::Placeholder
}
pub fn rounded(
clip_rect: LayoutRect,
radii: BorderRadius,
parent_index: ClipScrollNodeIndex,
) -> ClipScrollNode {
let complex_region = ComplexClipRegion {
rect: clip_rect,
radii,
mode: ClipMode::Clip,
};
ClipScrollNode {
parent_index,
clip: ClippingRegion::from_rect(clip_rect),
content_rect: LayoutRect::zero(), node_type: ClipScrollNodeType::Clip(ClipType::Rounded(complex_region)),
scroll_node_id: None,
clip_chain_id: None,
}
}
}
#[derive(Clone, Serialize)]
pub enum DisplayItem {
Rectangle(Box<CommonDisplayItem<wr::RectangleDisplayItem>>),
Text(Box<CommonDisplayItem<wr::TextDisplayItem, Vec<GlyphInstance>>>),
Image(Box<CommonDisplayItem<wr::ImageDisplayItem>>),
RepeatingImage(Box<CommonDisplayItem<wr::RepeatingImageDisplayItem>>),
Border(Box<CommonDisplayItem<wr::BorderDisplayItem, Vec<GradientStop>>>),
Gradient(Box<CommonDisplayItem<wr::GradientDisplayItem, Vec<GradientStop>>>),
RadialGradient(Box<CommonDisplayItem<wr::RadialGradientDisplayItem, Vec<GradientStop>>>),
Line(Box<CommonDisplayItem<wr::LineDisplayItem>>),
BoxShadow(Box<CommonDisplayItem<wr::BoxShadowDisplayItem>>),
PushTextShadow(Box<PushTextShadowDisplayItem>),
PopAllTextShadows(Box<PopAllTextShadowsDisplayItem>),
Iframe(Box<IframeDisplayItem>),
PushStackingContext(Box<PushStackingContextItem>),
PopStackingContext(Box<PopStackingContextItem>),
DefineClipScrollNode(Box<DefineClipScrollNodeItem>),
}
#[derive(Clone, Serialize)]
pub struct BaseDisplayItem {
pub metadata: DisplayItemMetadata,
pub clip_rect: LayoutRect,
pub section: DisplayListSection,
pub stacking_context_id: StackingContextId,
pub clipping_and_scrolling: ClippingAndScrolling,
}
impl BaseDisplayItem {
#[inline(always)]
pub fn new(
metadata: DisplayItemMetadata,
clip_rect: LayoutRect,
section: DisplayListSection,
stacking_context_id: StackingContextId,
clipping_and_scrolling: ClippingAndScrolling,
) -> BaseDisplayItem {
BaseDisplayItem {
metadata,
clip_rect,
section,
stacking_context_id,
clipping_and_scrolling,
}
}
#[inline(always)]
pub fn empty() -> BaseDisplayItem {
BaseDisplayItem {
metadata: DisplayItemMetadata {
node: OpaqueNode(0),
unique_id: 0,
cursor: None,
},
clip_rect: LayoutRect::max_rect(),
section: DisplayListSection::Content,
stacking_context_id: StackingContextId::root(),
clipping_and_scrolling: ClippingAndScrolling::simple(
ClipScrollNodeIndex::root_scroll_node(),
),
}
}
}
pub fn empty_common_item_properties() -> CommonItemProperties {
CommonItemProperties {
clip_rect: LayoutRect::max_rect(),
clip_chain_id: ClipChainId::INVALID,
spatial_id: SpatialId::root_scroll_node(wr::PipelineId::dummy()),
flags: PrimitiveFlags::empty(),
}
}
#[derive(Clone, PartialEq, Serialize)]
pub struct ClippingRegion {
pub main: LayoutRect,
}
impl ClippingRegion {
#[inline]
pub fn empty() -> ClippingRegion {
ClippingRegion {
main: LayoutRect::zero(),
}
}
#[inline]
pub fn max() -> ClippingRegion {
ClippingRegion {
main: LayoutRect::max_rect(),
}
}
#[inline]
pub fn from_rect(rect: LayoutRect) -> ClippingRegion {
ClippingRegion { main: rect }
}
}
impl fmt::Debug for ClippingRegion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if *self == ClippingRegion::max() {
write!(f, "ClippingRegion::Max")
} else if *self == ClippingRegion::empty() {
write!(f, "ClippingRegion::Empty")
} else {
write!(f, "ClippingRegion(Rect={:?})", self.main,)
}
}
}
#[derive(Clone, Copy, Serialize)]
pub struct DisplayItemMetadata {
pub node: OpaqueNode,
pub unique_id: u64,
pub cursor: Option<Cursor>,
}
#[derive(Clone, Eq, PartialEq, Serialize)]
pub enum TextOrientation {
Upright,
SidewaysLeft,
SidewaysRight,
}
#[derive(Clone, Serialize)]
pub struct IframeDisplayItem {
pub base: BaseDisplayItem,
pub iframe: PipelineId,
pub bounds: LayoutRect,
}
#[derive(Clone, Serialize)]
pub struct CommonDisplayItem<T, U = ()> {
pub base: BaseDisplayItem,
pub item: T,
pub data: U,
}
impl<T> CommonDisplayItem<T> {
pub fn new(base: BaseDisplayItem, item: T) -> Box<CommonDisplayItem<T>> {
Box::new(CommonDisplayItem {
base,
item,
data: (),
})
}
}
impl<T, U> CommonDisplayItem<T, U> {
pub fn with_data(base: BaseDisplayItem, item: T, data: U) -> Box<CommonDisplayItem<T, U>> {
Box::new(CommonDisplayItem { base, item, data })
}
}
#[derive(Clone, Serialize)]
pub struct PushTextShadowDisplayItem {
pub base: BaseDisplayItem,
pub shadow: Shadow,
}
#[derive(Clone, Serialize)]
pub struct PopAllTextShadowsDisplayItem {
pub base: BaseDisplayItem,
}
#[derive(Clone, Serialize)]
pub struct PushStackingContextItem {
pub base: BaseDisplayItem,
pub stacking_context: StackingContext,
}
#[derive(Clone, Serialize)]
pub struct PopStackingContextItem {
pub base: BaseDisplayItem,
pub stacking_context_id: StackingContextId,
pub established_reference_frame: bool,
}
#[derive(Clone, Serialize)]
pub struct DefineClipScrollNodeItem {
pub base: BaseDisplayItem,
pub node_index: ClipScrollNodeIndex,
}
impl DisplayItem {
pub fn base(&self) -> &BaseDisplayItem {
match *self {
DisplayItem::Rectangle(ref rect) => &rect.base,
DisplayItem::Text(ref text) => &text.base,
DisplayItem::Image(ref image_item) => &image_item.base,
DisplayItem::RepeatingImage(ref image_item) => &image_item.base,
DisplayItem::Border(ref border) => &border.base,
DisplayItem::Gradient(ref gradient) => &gradient.base,
DisplayItem::RadialGradient(ref gradient) => &gradient.base,
DisplayItem::Line(ref line) => &line.base,
DisplayItem::BoxShadow(ref box_shadow) => &box_shadow.base,
DisplayItem::PushTextShadow(ref push_text_shadow) => &push_text_shadow.base,
DisplayItem::PopAllTextShadows(ref pop_text_shadow) => &pop_text_shadow.base,
DisplayItem::Iframe(ref iframe) => &iframe.base,
DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base,
DisplayItem::PopStackingContext(ref item) => &item.base,
DisplayItem::DefineClipScrollNode(ref item) => &item.base,
}
}
pub fn clipping_and_scrolling(&self) -> ClippingAndScrolling {
self.base().clipping_and_scrolling
}
pub fn stacking_context_id(&self) -> StackingContextId {
self.base().stacking_context_id
}
pub fn section(&self) -> DisplayListSection {
self.base().section
}
pub fn bounds(&self) -> LayoutRect {
match *self {
DisplayItem::Rectangle(ref item) => item.item.common.clip_rect,
DisplayItem::Text(ref item) => item.item.bounds,
DisplayItem::Image(ref item) => item.item.bounds,
DisplayItem::RepeatingImage(ref item) => item.item.bounds,
DisplayItem::Border(ref item) => item.item.bounds,
DisplayItem::Gradient(ref item) => item.item.bounds,
DisplayItem::RadialGradient(ref item) => item.item.bounds,
DisplayItem::Line(ref item) => item.item.area,
DisplayItem::BoxShadow(ref item) => item.item.box_bounds,
DisplayItem::PushTextShadow(_) => LayoutRect::zero(),
DisplayItem::PopAllTextShadows(_) => LayoutRect::zero(),
DisplayItem::Iframe(ref item) => item.bounds,
DisplayItem::PushStackingContext(ref item) => item.stacking_context.bounds,
DisplayItem::PopStackingContext(_) => LayoutRect::zero(),
DisplayItem::DefineClipScrollNode(_) => LayoutRect::zero(),
}
}
}
impl fmt::Debug for DisplayItem {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if let DisplayItem::PushStackingContext(ref item) = *self {
return write!(f, "PushStackingContext({:?})", item.stacking_context);
}
if let DisplayItem::PopStackingContext(ref item) = *self {
return write!(f, "PopStackingContext({:?}", item.stacking_context_id);
}
if let DisplayItem::DefineClipScrollNode(ref item) = *self {
return write!(f, "DefineClipScrollNode({:?}", item.node_index);
}
write!(
f,
"{} @ {:?} {:?}",
match *self {
DisplayItem::Rectangle(_) => "Rectangle",
DisplayItem::Text(_) => "Text",
DisplayItem::Image(_) => "Image",
DisplayItem::RepeatingImage(_) => "RepeatingImage",
DisplayItem::Border(_) => "Border",
DisplayItem::Gradient(_) => "Gradient",
DisplayItem::RadialGradient(_) => "RadialGradient",
DisplayItem::Line(_) => "Line",
DisplayItem::BoxShadow(_) => "BoxShadow",
DisplayItem::PushTextShadow(_) => "PushTextShadow",
DisplayItem::PopAllTextShadows(_) => "PopTextShadow",
DisplayItem::Iframe(_) => "Iframe",
DisplayItem::PushStackingContext(_) |
DisplayItem::PopStackingContext(_) |
DisplayItem::DefineClipScrollNode(_) => "",
},
self.bounds(),
self.base().clip_rect
)
}
}
#[derive(Clone, Copy, Serialize)]
pub struct WebRenderImageInfo {
pub width: u32,
pub height: u32,
pub key: Option<ImageKey>,
}
impl WebRenderImageInfo {
#[inline]
pub fn from_image(image: &Image) -> WebRenderImageInfo {
WebRenderImageInfo {
width: image.width,
height: image.height,
key: image.id,
}
}
}
pub type ScrollOffsetMap = HashMap<ExternalScrollId, Vector2D<f32, LayoutPixel>>;