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