1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 //! Servo heavily uses display lists, which are retained-mode lists of painting commands to 6 //! perform. Using a list instead of painting elements in immediate mode allows transforms, hit 7 //! testing, and invalidation to be performed using the same primitives as painting. It also allows 8 //! Servo to aggressively cull invisible and out-of-bounds painting elements, to reduce overdraw. 9 //! Finally, display lists allow tiles to be farmed out onto multiple CPUs and painted in parallel 10 //! (although this benefit does not apply to GPU-based painting). 11 //! 12 //! Display items describe relatively high-level drawing operations (for example, entire borders 13 //! and shadows instead of lines and blur operations), to reduce the amount of allocation required. 14 //! They are therefore not exactly analogous to constructs like Skia pictures, which consist of 15 //! low-level drawing primitives. 16 17 use euclid::{Vector2D, TypedRect, SideOffsets2D}; 18 use euclid::num::{One, Zero}; 19 use gfx_traits::{self, StackingContextId}; 20 use gfx_traits::print_tree::PrintTree; 21 use msg::constellation_msg::PipelineId; 22 use net_traits::image::base::{Image, PixelFormat}; 23 use servo_geometry::MaxRect; 24 use std::cmp::Ordering; 25 use std::collections::HashMap; 26 use std::f32; 27 use std::fmt; 28 use webrender_api::{BorderRadius, BorderWidths, BoxShadowClipMode, ClipMode, ColorF}; 29 use webrender_api::{ComplexClipRegion, ExtendMode, ExternalScrollId, FilterOp, FontInstanceKey}; 30 use webrender_api::{GlyphInstance, GradientStop, ImageBorder, ImageKey, ImageRendering}; 31 use webrender_api::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform, LayoutVector2D, LineStyle}; 32 use webrender_api::{LocalClip, MixBlendMode, NormalBorder, ScrollPolicy, ScrollSensitivity}; 33 use webrender_api::{StickyOffsetBounds, TransformStyle}; 34 35 pub use style::dom::OpaqueNode; 36 37 /// The factor that we multiply the blur radius by in order to inflate the boundaries of display 38 /// items that involve a blur. This ensures that the display item boundaries include all the ink. 39 pub static BLUR_INFLATION_FACTOR: i32 = 3; 40 41 /// An index into the vector of ClipScrollNodes. During WebRender conversion these nodes 42 /// are given ClipIds. 43 #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] 44 pub struct ClipScrollNodeIndex(pub usize); 45 46 impl ClipScrollNodeIndex { is_root_scroll_node(&self) -> bool47 pub fn is_root_scroll_node(&self) -> bool { 48 match *self { 49 ClipScrollNodeIndex(0) => true, 50 _ => false, 51 } 52 } 53 to_define_item(&self) -> DisplayItem54 pub fn to_define_item(&self) -> DisplayItem { 55 DisplayItem::DefineClipScrollNode(Box::new(DefineClipScrollNodeItem { 56 base: BaseDisplayItem::empty(), 57 node_index: *self, 58 })) 59 } 60 } 61 62 /// A set of indices into the clip scroll node vector for a given item. 63 #[derive(Clone, Copy, Debug, Deserialize, MallocSizeOf, PartialEq, Serialize)] 64 pub struct ClippingAndScrolling { 65 pub scrolling: ClipScrollNodeIndex, 66 pub clipping: Option<ClipScrollNodeIndex>, 67 } 68 69 impl ClippingAndScrolling { simple(scrolling: ClipScrollNodeIndex) -> ClippingAndScrolling70 pub fn simple(scrolling: ClipScrollNodeIndex) -> ClippingAndScrolling { 71 ClippingAndScrolling { 72 scrolling, 73 clipping: None, 74 } 75 } 76 new(scrolling: ClipScrollNodeIndex, clipping: ClipScrollNodeIndex) -> ClippingAndScrolling77 pub fn new(scrolling: ClipScrollNodeIndex, clipping: ClipScrollNodeIndex) -> ClippingAndScrolling { 78 ClippingAndScrolling { 79 scrolling, 80 clipping: Some(clipping), 81 } 82 } 83 } 84 85 #[derive(Deserialize, MallocSizeOf, Serialize)] 86 pub struct DisplayList { 87 pub list: Vec<DisplayItem>, 88 pub clip_scroll_nodes: Vec<ClipScrollNode>, 89 } 90 91 impl DisplayList { 92 /// Return the bounds of this display list based on the dimensions of the root 93 /// stacking context. bounds(&self) -> LayoutRect94 pub fn bounds(&self) -> LayoutRect { 95 match self.list.get(0) { 96 Some(&DisplayItem::PushStackingContext(ref item)) => item.stacking_context.bounds, 97 Some(_) => unreachable!("Root element of display list not stacking context."), 98 None => LayoutRect::zero(), 99 } 100 } 101 print(&self)102 pub fn print(&self) { 103 let mut print_tree = PrintTree::new("Display List".to_owned()); 104 self.print_with_tree(&mut print_tree); 105 } 106 print_with_tree(&self, print_tree: &mut PrintTree)107 pub fn print_with_tree(&self, print_tree: &mut PrintTree) { 108 print_tree.new_level("ClipScrollNodes".to_owned()); 109 for node in &self.clip_scroll_nodes { 110 print_tree.add_item(format!("{:?}", node)); 111 } 112 print_tree.end_level(); 113 114 print_tree.new_level("Items".to_owned()); 115 for item in &self.list { 116 print_tree.add_item(format!("{:?} StackingContext: {:?} {:?}", 117 item, 118 item.base().stacking_context_id, 119 item.clipping_and_scrolling()) 120 ); 121 } 122 print_tree.end_level(); 123 } 124 } 125 126 impl gfx_traits::DisplayList for DisplayList { 127 /// Analyze the display list to figure out if this may be the first 128 /// contentful paint (i.e. the display list contains items of type text, 129 /// image, non-white canvas or SVG). Used by metrics. is_contentful(&self) -> bool130 fn is_contentful(&self) -> bool { 131 for item in &self.list { 132 match item { 133 &DisplayItem::Text(_) | 134 &DisplayItem::Image(_) => { 135 return true 136 } 137 _ => (), 138 } 139 } 140 141 false 142 } 143 } 144 145 /// Display list sections that make up a stacking context. Each section here refers 146 /// to the steps in CSS 2.1 Appendix E. 147 /// 148 #[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize)] 149 pub enum DisplayListSection { 150 BackgroundAndBorders, 151 BlockBackgroundsAndBorders, 152 Content, 153 Outlines, 154 } 155 156 #[derive(Clone, Copy, Debug, Deserialize, Eq, MallocSizeOf, Ord, PartialEq, PartialOrd, Serialize)] 157 pub enum StackingContextType { 158 Real, 159 PseudoPositioned, 160 PseudoFloat, 161 } 162 163 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 164 /// Represents one CSS stacking context, which may or may not have a hardware layer. 165 pub struct StackingContext { 166 /// The ID of this StackingContext for uniquely identifying it. 167 pub id: StackingContextId, 168 169 /// The type of this StackingContext. Used for collecting and sorting. 170 pub context_type: StackingContextType, 171 172 /// The position and size of this stacking context. 173 pub bounds: LayoutRect, 174 175 /// The overflow rect for this stacking context in its coordinate system. 176 pub overflow: LayoutRect, 177 178 /// The `z-index` for this stacking context. 179 pub z_index: i32, 180 181 /// CSS filters to be applied to this stacking context (including opacity). 182 pub filters: Vec<FilterOp>, 183 184 /// The blend mode with which this stacking context blends with its backdrop. 185 pub mix_blend_mode: MixBlendMode, 186 187 /// A transform to be applied to this stacking context. 188 pub transform: Option<LayoutTransform>, 189 190 /// The transform style of this stacking context. 191 pub transform_style: TransformStyle, 192 193 /// The perspective matrix to be applied to children. 194 pub perspective: Option<LayoutTransform>, 195 196 /// The scroll policy of this layer. 197 pub scroll_policy: ScrollPolicy, 198 199 /// The clip and scroll info for this StackingContext. 200 pub parent_clipping_and_scrolling: ClippingAndScrolling, 201 } 202 203 impl StackingContext { 204 /// Creates a new stacking context. 205 #[inline] new(id: StackingContextId, context_type: StackingContextType, bounds: LayoutRect, overflow: LayoutRect, z_index: i32, filters: Vec<FilterOp>, mix_blend_mode: MixBlendMode, transform: Option<LayoutTransform>, transform_style: TransformStyle, perspective: Option<LayoutTransform>, scroll_policy: ScrollPolicy, parent_clipping_and_scrolling: ClippingAndScrolling) -> StackingContext206 pub fn new(id: StackingContextId, 207 context_type: StackingContextType, 208 bounds: LayoutRect, 209 overflow: LayoutRect, 210 z_index: i32, 211 filters: Vec<FilterOp>, 212 mix_blend_mode: MixBlendMode, 213 transform: Option<LayoutTransform>, 214 transform_style: TransformStyle, 215 perspective: Option<LayoutTransform>, 216 scroll_policy: ScrollPolicy, 217 parent_clipping_and_scrolling: ClippingAndScrolling) 218 -> StackingContext { 219 StackingContext { 220 id, 221 context_type, 222 bounds, 223 overflow, 224 z_index, 225 filters, 226 mix_blend_mode, 227 transform, 228 transform_style, 229 perspective, 230 scroll_policy, 231 parent_clipping_and_scrolling, 232 } 233 } 234 235 #[inline] root() -> StackingContext236 pub fn root() -> StackingContext { 237 StackingContext::new( 238 StackingContextId::root(), 239 StackingContextType::Real, 240 LayoutRect::zero(), 241 LayoutRect::zero(), 242 0, 243 vec![], 244 MixBlendMode::Normal, 245 None, 246 TransformStyle::Flat, 247 None, 248 ScrollPolicy::Scrollable, 249 ClippingAndScrolling::simple(ClipScrollNodeIndex(0)) 250 ) 251 } 252 to_display_list_items(self) -> (DisplayItem, DisplayItem)253 pub fn to_display_list_items(self) -> (DisplayItem, DisplayItem) { 254 let mut base_item = BaseDisplayItem::empty(); 255 base_item.stacking_context_id = self.id; 256 base_item.clipping_and_scrolling = self.parent_clipping_and_scrolling; 257 258 let pop_item = DisplayItem::PopStackingContext(Box::new( 259 PopStackingContextItem { 260 base: base_item.clone(), 261 stacking_context_id: self.id, 262 } 263 )); 264 265 let push_item = DisplayItem::PushStackingContext(Box::new( 266 PushStackingContextItem { 267 base: base_item, 268 stacking_context: self, 269 } 270 )); 271 272 (push_item, pop_item) 273 } 274 } 275 276 impl Ord for StackingContext { cmp(&self, other: &Self) -> Ordering277 fn cmp(&self, other: &Self) -> Ordering { 278 if self.z_index != 0 || other.z_index != 0 { 279 return self.z_index.cmp(&other.z_index); 280 } 281 282 match (self.context_type, other.context_type) { 283 (StackingContextType::PseudoFloat, StackingContextType::PseudoFloat) => Ordering::Equal, 284 (StackingContextType::PseudoFloat, _) => Ordering::Less, 285 (_, StackingContextType::PseudoFloat) => Ordering::Greater, 286 (_, _) => Ordering::Equal, 287 } 288 } 289 } 290 291 impl PartialOrd for StackingContext { partial_cmp(&self, other: &Self) -> Option<Ordering>292 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 293 Some(self.cmp(other)) 294 } 295 } 296 297 impl Eq for StackingContext {} 298 impl PartialEq for StackingContext { eq(&self, other: &Self) -> bool299 fn eq(&self, other: &Self) -> bool { 300 self.id == other.id 301 } 302 } 303 304 impl fmt::Debug for StackingContext { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result305 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 306 let type_string = if self.context_type == StackingContextType::Real { 307 "StackingContext" 308 } else { 309 "Pseudo-StackingContext" 310 }; 311 312 write!(f, "{} at {:?} with overflow {:?}: {:?}", 313 type_string, 314 self.bounds, 315 self.overflow, 316 self.id) 317 } 318 } 319 320 #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] 321 pub struct StickyFrameData { 322 pub margins: SideOffsets2D<Option<f32>>, 323 pub vertical_offset_bounds: StickyOffsetBounds, 324 pub horizontal_offset_bounds: StickyOffsetBounds, 325 } 326 327 #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] 328 pub enum ClipScrollNodeType { 329 ScrollFrame(ScrollSensitivity, ExternalScrollId), 330 StickyFrame(StickyFrameData), 331 Clip, 332 } 333 334 /// Defines a clip scroll node. 335 #[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)] 336 pub struct ClipScrollNode { 337 /// The index of the parent of this ClipScrollNode. 338 pub parent_index: ClipScrollNodeIndex, 339 340 /// The position of this scroll root's frame in the parent stacking context. 341 pub clip: ClippingRegion, 342 343 /// The rect of the contents that can be scrolled inside of the scroll root. 344 pub content_rect: LayoutRect, 345 346 /// The type of this ClipScrollNode. 347 pub node_type: ClipScrollNodeType, 348 } 349 350 /// One drawing command in the list. 351 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 352 pub enum DisplayItem { 353 SolidColor(Box<SolidColorDisplayItem>), 354 Text(Box<TextDisplayItem>), 355 Image(Box<ImageDisplayItem>), 356 Border(Box<BorderDisplayItem>), 357 Gradient(Box<GradientDisplayItem>), 358 RadialGradient(Box<RadialGradientDisplayItem>), 359 Line(Box<LineDisplayItem>), 360 BoxShadow(Box<BoxShadowDisplayItem>), 361 PushTextShadow(Box<PushTextShadowDisplayItem>), 362 PopAllTextShadows(Box<PopAllTextShadowsDisplayItem>), 363 Iframe(Box<IframeDisplayItem>), 364 PushStackingContext(Box<PushStackingContextItem>), 365 PopStackingContext(Box<PopStackingContextItem>), 366 DefineClipScrollNode(Box<DefineClipScrollNodeItem>), 367 } 368 369 /// Information common to all display items. 370 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 371 pub struct BaseDisplayItem { 372 /// The boundaries of the display item, in layer coordinates. 373 pub bounds: LayoutRect, 374 375 /// Metadata attached to this display item. 376 pub metadata: DisplayItemMetadata, 377 378 /// The local clip for this item. 379 pub local_clip: LocalClip, 380 381 /// The section of the display list that this item belongs to. 382 pub section: DisplayListSection, 383 384 /// The id of the stacking context this item belongs to. 385 pub stacking_context_id: StackingContextId, 386 387 /// The clip and scroll info for this item. 388 pub clipping_and_scrolling: ClippingAndScrolling, 389 } 390 391 impl BaseDisplayItem { 392 #[inline(always)] new(bounds: LayoutRect, metadata: DisplayItemMetadata, local_clip: LocalClip, section: DisplayListSection, stacking_context_id: StackingContextId, clipping_and_scrolling: ClippingAndScrolling) -> BaseDisplayItem393 pub fn new(bounds: LayoutRect, 394 metadata: DisplayItemMetadata, 395 local_clip: LocalClip, 396 section: DisplayListSection, 397 stacking_context_id: StackingContextId, 398 clipping_and_scrolling: ClippingAndScrolling) 399 -> BaseDisplayItem { 400 BaseDisplayItem { 401 bounds, 402 metadata, 403 local_clip, 404 section, 405 stacking_context_id, 406 clipping_and_scrolling, 407 } 408 } 409 410 #[inline(always)] empty() -> BaseDisplayItem411 pub fn empty() -> BaseDisplayItem { 412 BaseDisplayItem { 413 bounds: TypedRect::zero(), 414 metadata: DisplayItemMetadata { 415 node: OpaqueNode(0), 416 pointing: None, 417 }, 418 // Create a rectangle of maximal size. 419 local_clip: LocalClip::from(LayoutRect::max_rect()), 420 section: DisplayListSection::Content, 421 stacking_context_id: StackingContextId::root(), 422 clipping_and_scrolling: ClippingAndScrolling::simple(ClipScrollNodeIndex(0)), 423 } 424 } 425 } 426 427 /// A clipping region for a display item. Currently, this can describe rectangles, rounded 428 /// rectangles (for `border-radius`), or arbitrary intersections of the two. Arbitrary transforms 429 /// are not supported because those are handled by the higher-level `StackingContext` abstraction. 430 #[derive(Clone, Deserialize, MallocSizeOf, PartialEq, Serialize)] 431 pub struct ClippingRegion { 432 /// The main rectangular region. This does not include any corners. 433 pub main: LayoutRect, 434 /// Any complex regions. 435 /// 436 /// TODO(pcwalton): Atomically reference count these? Not sure if it's worth the trouble. 437 /// Measure and follow up. 438 pub complex: Vec<ComplexClipRegion>, 439 } 440 441 impl ClippingRegion { 442 /// Returns an empty clipping region that, if set, will result in no pixels being visible. 443 #[inline] empty() -> ClippingRegion444 pub fn empty() -> ClippingRegion { 445 ClippingRegion { 446 main: LayoutRect::zero(), 447 complex: Vec::new(), 448 } 449 } 450 451 /// Returns an all-encompassing clipping region that clips no pixels out. 452 #[inline] max() -> ClippingRegion453 pub fn max() -> ClippingRegion { 454 ClippingRegion { 455 main: LayoutRect::max_rect(), 456 complex: Vec::new(), 457 } 458 } 459 460 /// Returns a clipping region that represents the given rectangle. 461 #[inline] from_rect(rect: LayoutRect) -> ClippingRegion462 pub fn from_rect(rect: LayoutRect) -> ClippingRegion { 463 ClippingRegion { 464 main: rect, 465 complex: Vec::new(), 466 } 467 } 468 469 /// Mutates this clipping region to intersect with the given rectangle. 470 /// 471 /// TODO(pcwalton): This could more eagerly eliminate complex clipping regions, at the cost of 472 /// complexity. 473 #[inline] intersect_rect(&mut self, rect: &LayoutRect)474 pub fn intersect_rect(&mut self, rect: &LayoutRect) { 475 self.main = self.main.intersection(rect).unwrap_or(LayoutRect::zero()) 476 } 477 478 /// Returns true if this clipping region might be nonempty. This can return false positives, 479 /// but never false negatives. 480 #[inline] might_be_nonempty(&self) -> bool481 pub fn might_be_nonempty(&self) -> bool { 482 !self.main.is_empty() 483 } 484 485 /// Returns true if this clipping region might contain the given point and false otherwise. 486 /// This is a quick, not a precise, test; it can yield false positives. 487 #[inline] might_intersect_point(&self, point: &LayoutPoint) -> bool488 pub fn might_intersect_point(&self, point: &LayoutPoint) -> bool { 489 self.main.contains(point) && 490 self.complex.iter().all(|complex| complex.rect.contains(point)) 491 } 492 493 /// Returns true if this clipping region might intersect the given rectangle and false 494 /// otherwise. This is a quick, not a precise, test; it can yield false positives. 495 #[inline] might_intersect_rect(&self, rect: &LayoutRect) -> bool496 pub fn might_intersect_rect(&self, rect: &LayoutRect) -> bool { 497 self.main.intersects(rect) && 498 self.complex.iter().all(|complex| complex.rect.intersects(rect)) 499 } 500 501 /// Returns true if this clipping region completely surrounds the given rect. 502 #[inline] does_not_clip_rect(&self, rect: &LayoutRect) -> bool503 pub fn does_not_clip_rect(&self, rect: &LayoutRect) -> bool { 504 self.main.contains(&rect.origin) && self.main.contains(&rect.bottom_right()) && 505 self.complex.iter().all(|complex| { 506 complex.rect.contains(&rect.origin) && complex.rect.contains(&rect.bottom_right()) 507 }) 508 } 509 510 /// Returns a bounding rect that surrounds this entire clipping region. 511 #[inline] bounding_rect(&self) -> LayoutRect512 pub fn bounding_rect(&self) -> LayoutRect { 513 let mut rect = self.main; 514 for complex in &*self.complex { 515 rect = rect.union(&complex.rect) 516 } 517 rect 518 } 519 520 /// Intersects this clipping region with the given rounded rectangle. 521 #[inline] intersect_with_rounded_rect(&mut self, rect: LayoutRect, radii: BorderRadius)522 pub fn intersect_with_rounded_rect(&mut self, rect: LayoutRect, radii: BorderRadius) { 523 let new_complex_region = ComplexClipRegion { 524 rect, 525 radii, 526 mode: ClipMode::Clip, 527 }; 528 529 // FIXME(pcwalton): This is O(n²) worst case for disjoint clipping regions. Is that OK? 530 // They're slow anyway… 531 // 532 // Possibly relevant if we want to do better: 533 // 534 // http://www.inrg.csie.ntu.edu.tw/algorithm2014/presentation/D&C%20Lee-84.pdf 535 for existing_complex_region in &mut self.complex { 536 if existing_complex_region.completely_encloses(&new_complex_region) { 537 *existing_complex_region = new_complex_region; 538 return 539 } 540 if new_complex_region.completely_encloses(existing_complex_region) { 541 return 542 } 543 } 544 545 self.complex.push(new_complex_region); 546 } 547 548 /// Translates this clipping region by the given vector. 549 #[inline] translate(&self, delta: &LayoutVector2D) -> ClippingRegion550 pub fn translate(&self, delta: &LayoutVector2D) -> ClippingRegion { 551 ClippingRegion { 552 main: self.main.translate(delta), 553 complex: self.complex.iter().map(|complex| { 554 ComplexClipRegion { 555 rect: complex.rect.translate(delta), 556 radii: complex.radii, 557 mode: complex.mode, 558 } 559 }).collect(), 560 } 561 } 562 563 #[inline] is_max(&self) -> bool564 pub fn is_max(&self) -> bool { 565 self.main == LayoutRect::max_rect() && self.complex.is_empty() 566 } 567 } 568 569 impl fmt::Debug for ClippingRegion { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result570 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 571 if *self == ClippingRegion::max() { 572 write!(f, "ClippingRegion::Max") 573 } else if *self == ClippingRegion::empty() { 574 write!(f, "ClippingRegion::Empty") 575 } else if self.main == LayoutRect::max_rect() { 576 write!(f, "ClippingRegion(Complex={:?})", self.complex) 577 } else { 578 write!(f, "ClippingRegion(Rect={:?}, Complex={:?})", self.main, self.complex) 579 } 580 } 581 } 582 583 pub trait CompletelyEncloses { completely_encloses(&self, other: &Self) -> bool584 fn completely_encloses(&self, other: &Self) -> bool; 585 } 586 587 impl CompletelyEncloses for ComplexClipRegion { 588 // TODO(pcwalton): This could be more aggressive by considering points that touch the inside of 589 // the border radius ellipse. completely_encloses(&self, other: &Self) -> bool590 fn completely_encloses(&self, other: &Self) -> bool { 591 let left = self.radii.top_left.width.max(self.radii.bottom_left.width); 592 let top = self.radii.top_left.height.max(self.radii.top_right.height); 593 let right = self.radii.top_right.width.max(self.radii.bottom_right.width); 594 let bottom = self.radii.bottom_left.height.max(self.radii.bottom_right.height); 595 let interior = LayoutRect::new(LayoutPoint::new(self.rect.origin.x + left, self.rect.origin.y + top), 596 LayoutSize::new(self.rect.size.width - left - right, 597 self.rect.size.height - top - bottom)); 598 interior.origin.x <= other.rect.origin.x && interior.origin.y <= other.rect.origin.y && 599 interior.max_x() >= other.rect.max_x() && interior.max_y() >= other.rect.max_y() 600 } 601 } 602 603 /// Metadata attached to each display item. This is useful for performing auxiliary threads with 604 /// the display list involving hit testing: finding the originating DOM node and determining the 605 /// cursor to use when the element is hovered over. 606 #[derive(Clone, Copy, Deserialize, MallocSizeOf, Serialize)] 607 pub struct DisplayItemMetadata { 608 /// The DOM node from which this display item originated. 609 pub node: OpaqueNode, 610 /// The value of the `cursor` property when the mouse hovers over this display item. If `None`, 611 /// this display item is ineligible for pointer events (`pointer-events: none`). 612 pub pointing: Option<u16>, 613 } 614 615 /// Paints a solid color. 616 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 617 pub struct SolidColorDisplayItem { 618 /// Fields common to all display items. 619 pub base: BaseDisplayItem, 620 621 /// The color. 622 pub color: ColorF, 623 } 624 625 /// Paints text. 626 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 627 pub struct TextDisplayItem { 628 /// Fields common to all display items. 629 pub base: BaseDisplayItem, 630 /// A collection of (non-whitespace) glyphs to be displayed. 631 pub glyphs: Vec<GlyphInstance>, 632 /// Reference to the font to be used. 633 pub font_key: FontInstanceKey, 634 /// The color of the text. 635 pub text_color: ColorF, 636 } 637 638 #[derive(Clone, Deserialize, Eq, MallocSizeOf, PartialEq, Serialize)] 639 pub enum TextOrientation { 640 Upright, 641 SidewaysLeft, 642 SidewaysRight, 643 } 644 645 /// Paints an image. 646 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 647 pub struct ImageDisplayItem { 648 pub base: BaseDisplayItem, 649 650 pub webrender_image: WebRenderImageInfo, 651 652 /// The dimensions to which the image display item should be stretched. If this is smaller than 653 /// the bounds of this display item, then the image will be repeated in the appropriate 654 /// direction to tile the entire bounds. 655 pub stretch_size: LayoutSize, 656 657 /// The amount of space to add to the right and bottom part of each tile, when the image 658 /// is tiled. 659 pub tile_spacing: LayoutSize, 660 661 /// The algorithm we should use to stretch the image. See `image_rendering` in CSS-IMAGES-3 § 662 /// 5.3. 663 pub image_rendering: ImageRendering, 664 } 665 /// Paints an iframe. 666 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 667 pub struct IframeDisplayItem { 668 pub base: BaseDisplayItem, 669 pub iframe: PipelineId, 670 } 671 672 /// Paints a gradient. 673 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 674 pub struct Gradient { 675 /// The start point of the gradient (computed during display list construction). 676 pub start_point: LayoutPoint, 677 678 /// The end point of the gradient (computed during display list construction). 679 pub end_point: LayoutPoint, 680 681 /// A list of color stops. 682 pub stops: Vec<GradientStop>, 683 684 /// Whether the gradient is repeated or clamped. 685 pub extend_mode: ExtendMode, 686 } 687 688 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 689 pub struct GradientDisplayItem { 690 /// Fields common to all display item. 691 pub base: BaseDisplayItem, 692 693 /// Contains all gradient data. Included start, end point and color stops. 694 pub gradient: Gradient, 695 696 /// The size of a single gradient tile. 697 /// 698 /// The gradient may fill an entire element background 699 /// but it can be composed from many smaller copys of 700 /// the same gradient. 701 /// 702 /// Without tiles, the tile will be the same size as the background. 703 pub tile: LayoutSize, 704 pub tile_spacing: LayoutSize, 705 } 706 707 /// Paints a radial gradient. 708 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 709 pub struct RadialGradient { 710 /// The center point of the gradient. 711 pub center: LayoutPoint, 712 713 /// The radius of the gradient with an x and an y component. 714 pub radius: LayoutSize, 715 716 /// A list of color stops. 717 pub stops: Vec<GradientStop>, 718 719 /// Whether the gradient is repeated or clamped. 720 pub extend_mode: ExtendMode, 721 } 722 723 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 724 pub struct RadialGradientDisplayItem { 725 /// Fields common to all display item. 726 pub base: BaseDisplayItem, 727 728 /// Contains all gradient data. 729 pub gradient: RadialGradient, 730 731 /// The size of a single gradient tile. 732 /// 733 /// The gradient may fill an entire element background 734 /// but it can be composed from many smaller copys of 735 /// the same gradient. 736 /// 737 /// Without tiles, the tile will be the same size as the background. 738 pub tile: LayoutSize, 739 pub tile_spacing: LayoutSize, 740 } 741 742 /// A border that is made of linear gradient 743 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 744 pub struct GradientBorder { 745 /// The gradient info that this border uses, border-image-source. 746 pub gradient: Gradient, 747 748 /// Outsets for the border, as per border-image-outset. 749 pub outset: SideOffsets2D<f32>, 750 } 751 752 /// A border that is made of radial gradient 753 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 754 pub struct RadialGradientBorder { 755 /// The gradient info that this border uses, border-image-source. 756 pub gradient: RadialGradient, 757 758 /// Outsets for the border, as per border-image-outset. 759 pub outset: SideOffsets2D<f32>, 760 } 761 762 /// Specifies the type of border 763 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 764 pub enum BorderDetails { 765 Normal(NormalBorder), 766 Image(ImageBorder), 767 Gradient(GradientBorder), 768 RadialGradient(RadialGradientBorder), 769 } 770 771 /// Paints a border. 772 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 773 pub struct BorderDisplayItem { 774 /// Fields common to all display items. 775 pub base: BaseDisplayItem, 776 777 /// Border widths. 778 pub border_widths: BorderWidths, 779 780 /// Details for specific border type 781 pub details: BorderDetails, 782 } 783 784 /// Paints a line segment. 785 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 786 pub struct LineDisplayItem { 787 pub base: BaseDisplayItem, 788 789 /// The line segment color. 790 pub color: ColorF, 791 792 /// The line segment style. 793 pub style: LineStyle, 794 } 795 796 /// Paints a box shadow per CSS-BACKGROUNDS. 797 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 798 pub struct BoxShadowDisplayItem { 799 /// Fields common to all display items. 800 pub base: BaseDisplayItem, 801 802 /// The dimensions of the box that we're placing a shadow around. 803 pub box_bounds: LayoutRect, 804 805 /// The offset of this shadow from the box. 806 pub offset: LayoutVector2D, 807 808 /// The color of this shadow. 809 pub color: ColorF, 810 811 /// The blur radius for this shadow. 812 pub blur_radius: f32, 813 814 /// The spread radius of this shadow. 815 pub spread_radius: f32, 816 817 /// The border radius of this shadow. 818 pub border_radius: BorderRadius, 819 820 /// How we should clip the result. 821 pub clip_mode: BoxShadowClipMode, 822 } 823 824 /// Defines a text shadow that affects all items until the paired PopTextShadow. 825 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 826 pub struct PushTextShadowDisplayItem { 827 /// Fields common to all display items. 828 pub base: BaseDisplayItem, 829 830 /// The offset of this shadow from the text. 831 pub offset: LayoutVector2D, 832 833 /// The color of this shadow. 834 pub color: ColorF, 835 836 /// The blur radius for this shadow. 837 pub blur_radius: f32, 838 } 839 840 /// Defines a text shadow that affects all items until the next PopTextShadow. 841 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 842 pub struct PopAllTextShadowsDisplayItem { 843 /// Fields common to all display items. 844 pub base: BaseDisplayItem, 845 } 846 847 /// Defines a stacking context. 848 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 849 pub struct PushStackingContextItem { 850 /// Fields common to all display items. 851 pub base: BaseDisplayItem, 852 853 pub stacking_context: StackingContext, 854 } 855 856 /// Defines a stacking context. 857 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 858 pub struct PopStackingContextItem { 859 /// Fields common to all display items. 860 pub base: BaseDisplayItem, 861 862 pub stacking_context_id: StackingContextId, 863 } 864 865 /// Starts a group of items inside a particular scroll root. 866 #[derive(Clone, Deserialize, MallocSizeOf, Serialize)] 867 pub struct DefineClipScrollNodeItem { 868 /// Fields common to all display items. 869 pub base: BaseDisplayItem, 870 871 /// The scroll root that this item starts. 872 pub node_index: ClipScrollNodeIndex, 873 } 874 875 impl DisplayItem { base(&self) -> &BaseDisplayItem876 pub fn base(&self) -> &BaseDisplayItem { 877 match *self { 878 DisplayItem::SolidColor(ref solid_color) => &solid_color.base, 879 DisplayItem::Text(ref text) => &text.base, 880 DisplayItem::Image(ref image_item) => &image_item.base, 881 DisplayItem::Border(ref border) => &border.base, 882 DisplayItem::Gradient(ref gradient) => &gradient.base, 883 DisplayItem::RadialGradient(ref gradient) => &gradient.base, 884 DisplayItem::Line(ref line) => &line.base, 885 DisplayItem::BoxShadow(ref box_shadow) => &box_shadow.base, 886 DisplayItem::PushTextShadow(ref push_text_shadow) => &push_text_shadow.base, 887 DisplayItem::PopAllTextShadows(ref pop_text_shadow) => &pop_text_shadow.base, 888 DisplayItem::Iframe(ref iframe) => &iframe.base, 889 DisplayItem::PushStackingContext(ref stacking_context) => &stacking_context.base, 890 DisplayItem::PopStackingContext(ref item) => &item.base, 891 DisplayItem::DefineClipScrollNode(ref item) => &item.base, 892 } 893 } 894 scroll_node_index(&self) -> ClipScrollNodeIndex895 pub fn scroll_node_index(&self) -> ClipScrollNodeIndex { 896 self.base().clipping_and_scrolling.scrolling 897 } 898 clipping_and_scrolling(&self) -> ClippingAndScrolling899 pub fn clipping_and_scrolling(&self) -> ClippingAndScrolling { 900 self.base().clipping_and_scrolling 901 } 902 stacking_context_id(&self) -> StackingContextId903 pub fn stacking_context_id(&self) -> StackingContextId { 904 self.base().stacking_context_id 905 } 906 section(&self) -> DisplayListSection907 pub fn section(&self) -> DisplayListSection { 908 self.base().section 909 } 910 bounds(&self) -> LayoutRect911 pub fn bounds(&self) -> LayoutRect { 912 self.base().bounds 913 } 914 debug_with_level(&self, level: u32)915 pub fn debug_with_level(&self, level: u32) { 916 let mut indent = String::new(); 917 for _ in 0..level { 918 indent.push_str("| ") 919 } 920 println!("{}+ {:?}", indent, self); 921 } 922 } 923 924 impl fmt::Debug for DisplayItem { fmt(&self, f: &mut fmt::Formatter) -> fmt::Result925 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 926 if let DisplayItem::PushStackingContext(ref item) = *self { 927 return write!(f, "PushStackingContext({:?})", item.stacking_context); 928 } 929 930 if let DisplayItem::PopStackingContext(ref item) = *self { 931 return write!(f, "PopStackingContext({:?}", item.stacking_context_id); 932 } 933 934 if let DisplayItem::DefineClipScrollNode(ref item) = *self { 935 return write!(f, "DefineClipScrollNode({:?}", item.node_index); 936 } 937 938 write!(f, "{} @ {:?} {:?}", 939 match *self { 940 DisplayItem::SolidColor(ref solid_color) => 941 format!("SolidColor rgba({}, {}, {}, {})", 942 solid_color.color.r, 943 solid_color.color.g, 944 solid_color.color.b, 945 solid_color.color.a), 946 DisplayItem::Text(_) => "Text".to_owned(), 947 DisplayItem::Image(_) => "Image".to_owned(), 948 DisplayItem::Border(_) => "Border".to_owned(), 949 DisplayItem::Gradient(_) => "Gradient".to_owned(), 950 DisplayItem::RadialGradient(_) => "RadialGradient".to_owned(), 951 DisplayItem::Line(_) => "Line".to_owned(), 952 DisplayItem::BoxShadow(_) => "BoxShadow".to_owned(), 953 DisplayItem::PushTextShadow(_) => "PushTextShadow".to_owned(), 954 DisplayItem::PopAllTextShadows(_) => "PopTextShadow".to_owned(), 955 DisplayItem::Iframe(_) => "Iframe".to_owned(), 956 DisplayItem::PushStackingContext(_) | 957 DisplayItem::PopStackingContext(_) | 958 DisplayItem::DefineClipScrollNode(_) => "".to_owned(), 959 }, 960 self.bounds(), 961 self.base().local_clip 962 ) 963 } 964 } 965 966 #[derive(Clone, Copy, Deserialize, MallocSizeOf, Serialize)] 967 pub struct WebRenderImageInfo { 968 pub width: u32, 969 pub height: u32, 970 pub format: PixelFormat, 971 pub key: Option<ImageKey>, 972 } 973 974 impl WebRenderImageInfo { 975 #[inline] from_image(image: &Image) -> WebRenderImageInfo976 pub fn from_image(image: &Image) -> WebRenderImageInfo { 977 WebRenderImageInfo { 978 width: image.width, 979 height: image.height, 980 format: image.format, 981 key: image.id, 982 } 983 } 984 } 985 986 /// The type of the scroll offset list. This is only populated if WebRender is in use. 987 pub type ScrollOffsetMap = HashMap<ExternalScrollId, Vector2D<f32>>; 988 989 990 pub trait SimpleMatrixDetection { is_identity_or_simple_translation(&self) -> bool991 fn is_identity_or_simple_translation(&self) -> bool; 992 } 993 994 impl SimpleMatrixDetection for LayoutTransform { 995 #[inline] is_identity_or_simple_translation(&self) -> bool996 fn is_identity_or_simple_translation(&self) -> bool { 997 let (_0, _1) = (Zero::zero(), One::one()); 998 self.m11 == _1 && self.m12 == _0 && self.m13 == _0 && self.m14 == _0 && 999 self.m21 == _0 && self.m22 == _1 && self.m23 == _0 && self.m24 == _0 && 1000 self.m31 == _0 && self.m32 == _0 && self.m33 == _1 && self.m34 == _0 && 1001 self.m44 == _1 1002 } 1003 } 1004 1005