1 
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 use api::{AlphaType, BorderDetails, BorderDisplayItem, BuiltDisplayListIter};
7 use api::{ClipAndScrollInfo, ClipId, ColorF, ComplexClipRegion, DeviceIntPoint, DeviceIntRect};
8 use api::{DeviceIntSize, DevicePixelScale, DeviceUintRect, DeviceUintSize};
9 use api::{DisplayItemRef, Epoch, ExtendMode, ExternalScrollId, FilterOp};
10 use api::{FontInstanceKey, FontRenderMode, GlyphInstance, GlyphOptions, GradientStop};
11 use api::{IframeDisplayItem, ImageDisplayItem, ImageKey, ImageRendering, ItemRange, LayerPoint};
12 use api::{LayerPrimitiveInfo, LayerRect, LayerSize, LayerVector2D, LayoutSize, LayoutTransform};
13 use api::{LayoutVector2D, LineOrientation, LineStyle, LocalClip, PipelineId};
14 use api::{PropertyBinding, RepeatMode, ScrollFrameDisplayItem, ScrollPolicy, ScrollSensitivity};
15 use api::{Shadow, SpecificDisplayItem, StackingContext, StickyFrameDisplayItem, TexelRect};
16 use api::{TileOffset, TransformStyle, YuvColorSpace, YuvData};
17 use app_units::Au;
18 use border::ImageBorderSegment;
19 use clip::{ClipRegion, ClipSource, ClipSources, ClipStore};
20 use clip_scroll_node::{ClipScrollNode, NodeType, StickyFrameInfo};
21 use clip_scroll_tree::{ClipChainIndex, ClipScrollNodeIndex, ClipScrollTree};
22 use euclid::{SideOffsets2D, rect, vec2};
23 use frame_builder::{FrameBuilder, FrameBuilderConfig};
24 use glyph_rasterizer::FontInstance;
25 use hit_test::{HitTestingItem, HitTestingRun};
26 use internal_types::{FastHashMap, FastHashSet};
27 use picture::{PictureCompositeMode, PictureKind, PicturePrimitive};
28 use prim_store::{BrushKind, BrushPrimitive, BrushSegmentDescriptor, CachedGradient};
29 use prim_store::{CachedGradientIndex, ImageCacheKey, ImagePrimitiveCpu, ImageSource};
30 use prim_store::{PrimitiveContainer, PrimitiveIndex, PrimitiveKind, PrimitiveStore};
31 use prim_store::{ScrollNodeAndClipChain, TextRunPrimitiveCpu};
32 use render_backend::{DocumentView};
33 use resource_cache::{FontInstanceMap, ImageRequest, TiledImageMap};
34 use scene::{Scene, ScenePipeline, StackingContextHelpers};
35 use scene_builder::{BuiltScene, SceneRequest};
36 use std::{f32, mem, usize};
37 use tiling::{CompositeOps, ScrollbarPrimitive};
38 use util::{MaxRect, RectHelpers, recycle_vec};
39 
40 static DEFAULT_SCROLLBAR_COLOR: ColorF = ColorF {
41     r: 0.3,
42     g: 0.3,
43     b: 0.3,
44     a: 0.6,
45 };
46 
47 /// A data structure that keeps track of mapping between API ClipIds and the indices used
48 /// internally in the ClipScrollTree to avoid having to do HashMap lookups. ClipIdToIndexMapper is
49 /// responsible for mapping both ClipId to ClipChainIndex and ClipId to ClipScrollNodeIndex.  We
50 /// also include two small LRU caches. Currently the caches are small (1 entry), but in the future
51 /// we could use uluru here to do something more involved.
52 #[derive(Default)]
53 pub struct ClipIdToIndexMapper {
54     /// A map which converts a ClipId for a clipping node or an API-defined ClipChain into
55     /// a ClipChainIndex, which is the index used internally in the ClipScrollTree to
56     /// identify ClipChains.
57     clip_chain_map: FastHashMap<ClipId, ClipChainIndex>,
58 
59     /// The last mapped ClipChainIndex, used to avoid having to do lots of consecutive
60     /// HashMap lookups.
61     cached_clip_chain_index: Option<(ClipId, ClipChainIndex)>,
62 
63     /// The offset in the ClipScrollTree's array of ClipScrollNodes for a particular pipeline.
64     /// This is used to convert a ClipId into a ClipScrollNodeIndex.
65     pipeline_offsets: FastHashMap<PipelineId, usize>,
66 
67     /// The last mapped pipeline offset for this mapper. This is used to avoid having to
68     /// consult `pipeline_offsets` repeatedly when flattening the display list.
69     cached_pipeline_offset: Option<(PipelineId, usize)>,
70 
71     /// The next available pipeline offset for ClipScrollNodeIndex. When we encounter a pipeline
72     /// we will use this value and increment it by the total number of ClipScrollNodes in the
73     /// pipeline's display list.
74     next_available_offset: usize,
75 }
76 
77 impl ClipIdToIndexMapper {
add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex)78     pub fn add_clip_chain(&mut self, id: ClipId, index: ClipChainIndex) {
79         debug_assert!(!self.clip_chain_map.contains_key(&id));
80         self.clip_chain_map.insert(id, index);
81     }
82 
map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId)83     pub fn map_to_parent_clip_chain(&mut self, id: ClipId, parent_id: &ClipId) {
84         let parent_chain_index = self.get_clip_chain_index(parent_id);
85         self.add_clip_chain(id, parent_chain_index);
86     }
87 
get_clip_chain_index(&mut self, id: &ClipId) -> ClipChainIndex88     pub fn get_clip_chain_index(&mut self, id: &ClipId) -> ClipChainIndex {
89         match self.cached_clip_chain_index {
90             Some((cached_id, cached_clip_chain_index)) if cached_id == *id =>
91                 return cached_clip_chain_index,
92             _ => {}
93         }
94 
95         self.clip_chain_map[id]
96     }
97 
get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex98     pub fn get_clip_chain_index_and_cache_result(&mut self, id: &ClipId) -> ClipChainIndex {
99         let index = self.get_clip_chain_index(id);
100         self.cached_clip_chain_index = Some((*id, index));
101         index
102     }
103 
map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain104     pub fn map_clip_and_scroll(&mut self, info: &ClipAndScrollInfo) -> ScrollNodeAndClipChain {
105         ScrollNodeAndClipChain::new(
106             self.get_node_index(info.scroll_node_id),
107             self.get_clip_chain_index_and_cache_result(&info.clip_node_id())
108         )
109     }
110 
simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain111     pub fn simple_scroll_and_clip_chain(&mut self, id: &ClipId) -> ScrollNodeAndClipChain {
112         self.map_clip_and_scroll(&ClipAndScrollInfo::simple(*id))
113     }
114 
initialize_for_pipeline(&mut self, pipeline: &ScenePipeline)115     pub fn initialize_for_pipeline(&mut self, pipeline: &ScenePipeline) {
116         debug_assert!(!self.pipeline_offsets.contains_key(&pipeline.pipeline_id));
117         self.pipeline_offsets.insert(pipeline.pipeline_id, self.next_available_offset);
118         self.next_available_offset += pipeline.display_list.total_clip_ids();
119     }
120 
get_node_index(&mut self, id: ClipId) -> ClipScrollNodeIndex121     pub fn get_node_index(&mut self, id: ClipId) -> ClipScrollNodeIndex {
122         let (index, pipeline_id) = match id {
123             ClipId::Clip(index, pipeline_id) => (index, pipeline_id),
124             ClipId::ClipChain(_) => panic!("Tried to use ClipChain as scroll node."),
125         };
126 
127         let pipeline_offset = match self.cached_pipeline_offset {
128             Some((last_used_id, offset)) if last_used_id == pipeline_id => offset,
129             _ => {
130                 let offset = self.pipeline_offsets[&pipeline_id];
131                 self.cached_pipeline_offset = Some((pipeline_id, offset));
132                 offset
133             }
134         };
135 
136         ClipScrollNodeIndex(pipeline_offset + index)
137     }
138 }
139 
140 /// A structure that converts a serialized display list into a form that WebRender
141 /// can use to later build a frame. This structure produces a FrameBuilder. Public
142 /// members are typically those that are destructured into the FrameBuilder.
143 pub struct DisplayListFlattener<'a> {
144     /// The scene that we are currently flattening.
145     scene: &'a Scene,
146 
147     /// The ClipScrollTree that we are currently building during flattening.
148     clip_scroll_tree: &'a mut ClipScrollTree,
149 
150     /// The map of all font instances.
151     font_instances: FontInstanceMap,
152 
153     /// The map of tiled images.
154     tiled_image_map: TiledImageMap,
155 
156     /// Used to track the latest flattened epoch for each pipeline.
157     pipeline_epochs: Vec<(PipelineId, Epoch)>,
158 
159     /// A set of pipelines that the caller has requested be made available as
160     /// output textures.
161     output_pipelines: &'a FastHashSet<PipelineId>,
162 
163     /// A list of replacements to make in order to properly handle fixed position
164     /// content as well as stacking contexts that create reference frames.
165     replacements: Vec<(ClipId, ClipId)>,
166 
167     /// The data structure that converting between ClipId and the various index
168     /// types that the ClipScrollTree uses.
169     id_to_index_mapper: ClipIdToIndexMapper,
170 
171     /// A stack of the current shadow primitives.  The sub-Vec stores
172     /// a buffer of fast-path primitives to be appended on pop.
173     shadow_prim_stack: Vec<(PrimitiveIndex, Vec<(PrimitiveIndex, ScrollNodeAndClipChain)>)>,
174 
175     /// A buffer of "real" content when doing fast-path shadows. This is appended
176     /// when the shadow stack is empty.
177     pending_shadow_contents: Vec<(PrimitiveIndex, ScrollNodeAndClipChain, LayerPrimitiveInfo)>,
178 
179     /// A stack of scroll nodes used during display list processing to properly
180     /// parent new scroll nodes.
181     reference_frame_stack: Vec<(ClipId, ClipScrollNodeIndex)>,
182 
183     /// A stack of stacking context properties.
184     sc_stack: Vec<FlattenedStackingContext>,
185 
186     /// A stack of the current pictures.
187     picture_stack: Vec<PrimitiveIndex>,
188 
189     /// A list of scrollbar primitives.
190     pub scrollbar_prims: Vec<ScrollbarPrimitive>,
191 
192     /// The store of primitives.
193     pub prim_store: PrimitiveStore,
194 
195     /// Information about all primitives involved in hit testing.
196     pub hit_testing_runs: Vec<HitTestingRun>,
197 
198     /// The store which holds all complex clipping information.
199     pub clip_store: ClipStore,
200 
201     /// The configuration to use for the FrameBuilder. We consult this in
202     /// order to determine the default font.
203     pub config: FrameBuilderConfig,
204 
205     /// The gradients collecting during display list flattening.
206     pub cached_gradients: Vec<CachedGradient>,
207 }
208 
209 impl<'a> DisplayListFlattener<'a> {
create_frame_builder( old_builder: FrameBuilder, scene: &Scene, clip_scroll_tree: &mut ClipScrollTree, font_instances: FontInstanceMap, tiled_image_map: TiledImageMap, view: &DocumentView, output_pipelines: &FastHashSet<PipelineId>, frame_builder_config: &FrameBuilderConfig, new_scene: &mut Scene, ) -> FrameBuilder210     pub fn create_frame_builder(
211         old_builder: FrameBuilder,
212         scene: &Scene,
213         clip_scroll_tree: &mut ClipScrollTree,
214         font_instances: FontInstanceMap,
215         tiled_image_map: TiledImageMap,
216         view: &DocumentView,
217         output_pipelines: &FastHashSet<PipelineId>,
218         frame_builder_config: &FrameBuilderConfig,
219         new_scene: &mut Scene,
220     ) -> FrameBuilder {
221         // We checked that the root pipeline is available on the render backend.
222         let root_pipeline_id = scene.root_pipeline_id.unwrap();
223         let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
224 
225         let root_epoch = scene.pipeline_epochs[&root_pipeline_id];
226 
227         let background_color = root_pipeline
228             .background_color
229             .and_then(|color| if color.a > 0.0 { Some(color) } else { None });
230 
231         let mut flattener = DisplayListFlattener {
232             scene,
233             clip_scroll_tree,
234             font_instances,
235             tiled_image_map,
236             config: *frame_builder_config,
237             pipeline_epochs: Vec::new(),
238             replacements: Vec::new(),
239             output_pipelines,
240             id_to_index_mapper: ClipIdToIndexMapper::default(),
241             hit_testing_runs: recycle_vec(old_builder.hit_testing_runs),
242             shadow_prim_stack: Vec::new(),
243             cached_gradients: recycle_vec(old_builder.cached_gradients),
244             pending_shadow_contents: Vec::new(),
245             scrollbar_prims: recycle_vec(old_builder.scrollbar_prims),
246             reference_frame_stack: Vec::new(),
247             picture_stack: Vec::new(),
248             sc_stack: Vec::new(),
249             prim_store: old_builder.prim_store.recycle(),
250             clip_store: old_builder.clip_store.recycle(),
251         };
252 
253         flattener.id_to_index_mapper.initialize_for_pipeline(root_pipeline);
254         flattener.push_root(
255             root_pipeline_id,
256             &root_pipeline.viewport_size,
257             &root_pipeline.content_size,
258         );
259         flattener.setup_viewport_offset(view.inner_rect, view.accumulated_scale_factor());
260         flattener.flatten_root(root_pipeline, &root_pipeline.viewport_size);
261 
262         debug_assert!(flattener.picture_stack.is_empty());
263 
264         new_scene.root_pipeline_id = Some(root_pipeline_id);
265         new_scene.pipeline_epochs.insert(root_pipeline_id, root_epoch);
266         new_scene.pipeline_epochs.extend(flattener.pipeline_epochs.drain(..));
267         new_scene.pipelines = scene.pipelines.clone();
268 
269         FrameBuilder::with_display_list_flattener(
270             view.inner_rect,
271             background_color,
272             view.window_size,
273             flattener
274         )
275     }
276 
277     /// Since WebRender still handles fixed position and reference frame content internally
278     /// we need to apply this table of id replacements only to the id that affects the
279     /// position of a node. We can eventually remove this when clients start handling
280     /// reference frames themselves. This method applies these replacements.
apply_scroll_frame_id_replacement(&self, index: ClipId) -> ClipId281     fn apply_scroll_frame_id_replacement(&self, index: ClipId) -> ClipId {
282         match self.replacements.last() {
283             Some(&(to_replace, replacement)) if to_replace == index => replacement,
284             _ => index,
285         }
286     }
287 
get_complex_clips( &self, pipeline_id: PipelineId, complex_clips: ItemRange<ComplexClipRegion>, ) -> Vec<ComplexClipRegion>288     fn get_complex_clips(
289         &self,
290         pipeline_id: PipelineId,
291         complex_clips: ItemRange<ComplexClipRegion>,
292     ) -> Vec<ComplexClipRegion> {
293         if complex_clips.is_empty() {
294             return vec![];
295         }
296 
297         self.scene
298             .pipelines
299             .get(&pipeline_id)
300             .expect("No display list?")
301             .display_list
302             .get(complex_clips)
303             .collect()
304     }
305 
get_clip_chain_items( &self, pipeline_id: PipelineId, items: ItemRange<ClipId>, ) -> Vec<ClipId>306     fn get_clip_chain_items(
307         &self,
308         pipeline_id: PipelineId,
309         items: ItemRange<ClipId>,
310     ) -> Vec<ClipId> {
311         if items.is_empty() {
312             return vec![];
313         }
314 
315         self.scene
316             .pipelines
317             .get(&pipeline_id)
318             .expect("No display list?")
319             .display_list
320             .get(items)
321             .collect()
322     }
323 
flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize)324     fn flatten_root(&mut self, pipeline: &'a ScenePipeline, frame_size: &LayoutSize) {
325         let pipeline_id = pipeline.pipeline_id;
326         let reference_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
327             &ClipId::root_reference_frame(pipeline_id)
328         );
329         let scroll_frame_info = self.id_to_index_mapper.simple_scroll_and_clip_chain(
330             &ClipId::root_scroll_node(pipeline_id)
331         );
332 
333         self.push_stacking_context(
334             pipeline_id,
335             CompositeOps::default(),
336             TransformStyle::Flat,
337             true,
338             true,
339             scroll_frame_info,
340         );
341 
342         // For the root pipeline, there's no need to add a full screen rectangle
343         // here, as it's handled by the framebuffer clear.
344         if self.scene.root_pipeline_id != Some(pipeline_id) {
345             if let Some(pipeline) = self.scene.pipelines.get(&pipeline_id) {
346                 if let Some(bg_color) = pipeline.background_color {
347                     let root_bounds = LayerRect::new(LayerPoint::zero(), *frame_size);
348                     let info = LayerPrimitiveInfo::new(root_bounds);
349                     self.add_solid_rectangle(
350                         reference_frame_info,
351                         &info,
352                         bg_color,
353                         None,
354                     );
355                 }
356             }
357         }
358 
359         self.flatten_items(&mut pipeline.display_list.iter(), pipeline_id, LayerVector2D::zero());
360 
361         if self.config.enable_scrollbars {
362             let scrollbar_rect = LayerRect::new(LayerPoint::zero(), LayerSize::new(10.0, 70.0));
363             let container_rect = LayerRect::new(LayerPoint::zero(), *frame_size);
364             self.add_scroll_bar(
365                 reference_frame_info,
366                 &LayerPrimitiveInfo::new(scrollbar_rect),
367                 DEFAULT_SCROLLBAR_COLOR,
368                 ScrollbarInfo(scroll_frame_info.scroll_node_id, container_rect),
369             );
370         }
371 
372         self.pop_stacking_context();
373     }
374 
flatten_items( &mut self, traversal: &mut BuiltDisplayListIter<'a>, pipeline_id: PipelineId, reference_frame_relative_offset: LayerVector2D, )375     fn flatten_items(
376         &mut self,
377         traversal: &mut BuiltDisplayListIter<'a>,
378         pipeline_id: PipelineId,
379         reference_frame_relative_offset: LayerVector2D,
380     ) {
381         loop {
382             let subtraversal = {
383                 let item = match traversal.next() {
384                     Some(item) => item,
385                     None => break,
386                 };
387 
388                 if SpecificDisplayItem::PopStackingContext == *item.item() {
389                     return;
390                 }
391 
392                 self.flatten_item(
393                     item,
394                     pipeline_id,
395                     reference_frame_relative_offset,
396                 )
397             };
398 
399             // If flatten_item created a sub-traversal, we need `traversal` to have the
400             // same state as the completed subtraversal, so we reinitialize it here.
401             if let Some(subtraversal) = subtraversal {
402                 *traversal = subtraversal;
403             }
404         }
405     }
406 
flatten_sticky_frame( &mut self, item: &DisplayItemRef, info: &StickyFrameDisplayItem, clip_and_scroll: &ScrollNodeAndClipChain, parent_id: &ClipId, reference_frame_relative_offset: &LayerVector2D, )407     fn flatten_sticky_frame(
408         &mut self,
409         item: &DisplayItemRef,
410         info: &StickyFrameDisplayItem,
411         clip_and_scroll: &ScrollNodeAndClipChain,
412         parent_id: &ClipId,
413         reference_frame_relative_offset: &LayerVector2D,
414     ) {
415         let frame_rect = item.rect().translate(&reference_frame_relative_offset);
416         let sticky_frame_info = StickyFrameInfo::new(
417             info.margins,
418             info.vertical_offset_bounds,
419             info.horizontal_offset_bounds,
420             info.previously_applied_offset,
421         );
422 
423         let index = self.id_to_index_mapper.get_node_index(info.id);
424         self.clip_scroll_tree.add_sticky_frame(
425             index,
426             clip_and_scroll.scroll_node_id, /* parent id */
427             frame_rect,
428             sticky_frame_info,
429             info.id.pipeline_id(),
430         );
431         self.id_to_index_mapper.map_to_parent_clip_chain(info.id, &parent_id);
432     }
433 
flatten_scroll_frame( &mut self, item: &DisplayItemRef, info: &ScrollFrameDisplayItem, pipeline_id: PipelineId, clip_and_scroll_ids: &ClipAndScrollInfo, reference_frame_relative_offset: &LayerVector2D, )434     fn flatten_scroll_frame(
435         &mut self,
436         item: &DisplayItemRef,
437         info: &ScrollFrameDisplayItem,
438         pipeline_id: PipelineId,
439         clip_and_scroll_ids: &ClipAndScrollInfo,
440         reference_frame_relative_offset: &LayerVector2D,
441     ) {
442         let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
443         let clip_region = ClipRegion::create_for_clip_node(
444             *item.local_clip().clip_rect(),
445             complex_clips,
446             info.image_mask,
447             &reference_frame_relative_offset,
448         );
449         // Just use clip rectangle as the frame rect for this scroll frame.
450         // This is useful when calculating scroll extents for the
451         // ClipScrollNode::scroll(..) API as well as for properly setting sticky
452         // positioning offsets.
453         let frame_rect = item.local_clip()
454             .clip_rect()
455             .translate(&reference_frame_relative_offset);
456         let content_rect = item.rect().translate(&reference_frame_relative_offset);
457 
458         debug_assert!(info.clip_id != info.scroll_frame_id);
459 
460         self.add_clip_node(info.clip_id, clip_and_scroll_ids.scroll_node_id, clip_region);
461 
462         self.add_scroll_frame(
463             info.scroll_frame_id,
464             info.clip_id,
465             info.external_id,
466             pipeline_id,
467             &frame_rect,
468             &content_rect.size,
469             info.scroll_sensitivity,
470         );
471     }
472 
flatten_stacking_context( &mut self, traversal: &mut BuiltDisplayListIter<'a>, pipeline_id: PipelineId, unreplaced_scroll_id: ClipId, mut scroll_node_id: ClipId, mut reference_frame_relative_offset: LayerVector2D, bounds: &LayerRect, stacking_context: &StackingContext, filters: ItemRange<FilterOp>, is_backface_visible: bool, )473     fn flatten_stacking_context(
474         &mut self,
475         traversal: &mut BuiltDisplayListIter<'a>,
476         pipeline_id: PipelineId,
477         unreplaced_scroll_id: ClipId,
478         mut scroll_node_id: ClipId,
479         mut reference_frame_relative_offset: LayerVector2D,
480         bounds: &LayerRect,
481         stacking_context: &StackingContext,
482         filters: ItemRange<FilterOp>,
483         is_backface_visible: bool,
484     ) {
485         // Avoid doing unnecessary work for empty stacking contexts.
486         if traversal.current_stacking_context_empty() {
487             traversal.skip_current_stacking_context();
488             return;
489         }
490 
491         let composition_operations = {
492             // TODO(optimization?): self.traversal.display_list()
493             let display_list = &self
494                 .scene
495                 .pipelines
496                 .get(&pipeline_id)
497                 .expect("No display list?!")
498                 .display_list;
499             CompositeOps::new(
500                 stacking_context.filter_ops_for_compositing(display_list, filters),
501                 stacking_context.mix_blend_mode_for_compositing(),
502             )
503         };
504 
505         if stacking_context.scroll_policy == ScrollPolicy::Fixed {
506             scroll_node_id = self.current_reference_frame_id();
507             self.replacements.push((unreplaced_scroll_id, scroll_node_id));
508         }
509 
510         reference_frame_relative_offset += bounds.origin.to_vector();
511 
512         // If we have a transformation or a perspective, we should have been assigned a new
513         // reference frame id. This means this stacking context establishes a new reference frame.
514         // Descendant fixed position content will be positioned relative to us.
515         if let Some(reference_frame_id) = stacking_context.reference_frame_id {
516             debug_assert!(
517                 stacking_context.transform.is_some() ||
518                 stacking_context.perspective.is_some()
519             );
520 
521             let reference_frame_bounds = LayerRect::new(LayerPoint::zero(), bounds.size);
522             self.push_reference_frame(
523                 reference_frame_id,
524                 Some(scroll_node_id),
525                 pipeline_id,
526                 &reference_frame_bounds,
527                 stacking_context.transform,
528                 stacking_context.perspective,
529                 reference_frame_relative_offset,
530             );
531             self.replacements.push((unreplaced_scroll_id, reference_frame_id));
532             reference_frame_relative_offset = LayerVector2D::zero();
533         }
534 
535         // We apply the replacements one more time in case we need to set it to a replacement
536         // that we just pushed above.
537         let sc_scroll_node = self.apply_scroll_frame_id_replacement(unreplaced_scroll_id);
538         let stacking_context_clip_and_scroll =
539             self.id_to_index_mapper.simple_scroll_and_clip_chain(&sc_scroll_node);
540         self.push_stacking_context(
541             pipeline_id,
542             composition_operations,
543             stacking_context.transform_style,
544             is_backface_visible,
545             false,
546             stacking_context_clip_and_scroll,
547         );
548 
549         self.flatten_items(
550             traversal,
551             pipeline_id,
552             reference_frame_relative_offset,
553         );
554 
555         if stacking_context.scroll_policy == ScrollPolicy::Fixed {
556             self.replacements.pop();
557         }
558 
559         if stacking_context.reference_frame_id.is_some() {
560             self.replacements.pop();
561             self.pop_reference_frame();
562         }
563 
564         self.pop_stacking_context();
565     }
566 
flatten_iframe( &mut self, item: &DisplayItemRef, info: &IframeDisplayItem, clip_and_scroll_ids: &ClipAndScrollInfo, reference_frame_relative_offset: &LayerVector2D, )567     fn flatten_iframe(
568         &mut self,
569         item: &DisplayItemRef,
570         info: &IframeDisplayItem,
571         clip_and_scroll_ids: &ClipAndScrollInfo,
572         reference_frame_relative_offset: &LayerVector2D,
573     ) {
574         let iframe_pipeline_id = info.pipeline_id;
575         let pipeline = match self.scene.pipelines.get(&iframe_pipeline_id) {
576             Some(pipeline) => pipeline,
577             None => return,
578         };
579 
580         self.id_to_index_mapper.initialize_for_pipeline(pipeline);
581 
582         self.add_clip_node(
583             info.clip_id,
584             clip_and_scroll_ids.scroll_node_id,
585             ClipRegion::create_for_clip_node_with_local_clip(
586                 &item.local_clip(),
587                 &reference_frame_relative_offset
588             ),
589         );
590 
591         let epoch = self.scene.pipeline_epochs[&iframe_pipeline_id];
592         self.pipeline_epochs.push((iframe_pipeline_id, epoch));
593 
594         let bounds = item.rect();
595         let iframe_rect = LayerRect::new(LayerPoint::zero(), bounds.size);
596         let origin = *reference_frame_relative_offset + bounds.origin.to_vector();
597         self.push_reference_frame(
598             ClipId::root_reference_frame(iframe_pipeline_id),
599             Some(info.clip_id),
600             iframe_pipeline_id,
601             &iframe_rect,
602             None,
603             None,
604             origin,
605         );
606 
607         self.add_scroll_frame(
608             ClipId::root_scroll_node(iframe_pipeline_id),
609             ClipId::root_reference_frame(iframe_pipeline_id),
610             Some(ExternalScrollId(0, iframe_pipeline_id)),
611             iframe_pipeline_id,
612             &iframe_rect,
613             &pipeline.content_size,
614             ScrollSensitivity::ScriptAndInputEvents,
615         );
616 
617         self.flatten_root(pipeline, &iframe_rect.size);
618 
619         self.pop_reference_frame();
620     }
621 
flatten_item<'b>( &'b mut self, item: DisplayItemRef<'a, 'b>, pipeline_id: PipelineId, reference_frame_relative_offset: LayerVector2D, ) -> Option<BuiltDisplayListIter<'a>>622     fn flatten_item<'b>(
623         &'b mut self,
624         item: DisplayItemRef<'a, 'b>,
625         pipeline_id: PipelineId,
626         reference_frame_relative_offset: LayerVector2D,
627     ) -> Option<BuiltDisplayListIter<'a>> {
628         let mut clip_and_scroll_ids = item.clip_and_scroll();
629         let unreplaced_scroll_id = clip_and_scroll_ids.scroll_node_id;
630         clip_and_scroll_ids.scroll_node_id =
631             self.apply_scroll_frame_id_replacement(clip_and_scroll_ids.scroll_node_id);
632         let clip_and_scroll = self.id_to_index_mapper.map_clip_and_scroll(&clip_and_scroll_ids);
633 
634         let prim_info = item.get_layer_primitive_info(&reference_frame_relative_offset);
635         match *item.item() {
636             SpecificDisplayItem::Image(ref info) => {
637                 match self.tiled_image_map.get(&info.image_key).cloned() {
638                     Some(tiling) => {
639                         // The image resource is tiled. We have to generate an image primitive
640                         // for each tile.
641                         self.decompose_image(
642                             clip_and_scroll,
643                             &prim_info,
644                             info,
645                             tiling.image_size,
646                             tiling.tile_size as u32,
647                         );
648                     }
649                     None => {
650                         self.add_image(
651                             clip_and_scroll,
652                             &prim_info,
653                             info.stretch_size,
654                             info.tile_spacing,
655                             None,
656                             info.image_key,
657                             info.image_rendering,
658                             info.alpha_type,
659                             None,
660                         );
661                     }
662                 }
663             }
664             SpecificDisplayItem::YuvImage(ref info) => {
665                 self.add_yuv_image(
666                     clip_and_scroll,
667                     &prim_info,
668                     info.yuv_data,
669                     info.color_space,
670                     info.image_rendering,
671                 );
672             }
673             SpecificDisplayItem::Text(ref text_info) => {
674                 self.add_text(
675                     clip_and_scroll,
676                     reference_frame_relative_offset,
677                     &prim_info,
678                     &text_info.font_key,
679                     &text_info.color,
680                     item.glyphs(),
681                     item.display_list().get(item.glyphs()).count(),
682                     text_info.glyph_options,
683                 );
684             }
685             SpecificDisplayItem::Rectangle(ref info) => {
686                 self.add_solid_rectangle(
687                     clip_and_scroll,
688                     &prim_info,
689                     info.color,
690                     None,
691                 );
692             }
693             SpecificDisplayItem::ClearRectangle => {
694                 self.add_clear_rectangle(
695                     clip_and_scroll,
696                     &prim_info,
697                 );
698             }
699             SpecificDisplayItem::Line(ref info) => {
700                 self.add_line(
701                     clip_and_scroll,
702                     &prim_info,
703                     info.wavy_line_thickness,
704                     info.orientation,
705                     &info.color,
706                     info.style,
707                 );
708             }
709             SpecificDisplayItem::Gradient(ref info) => {
710                 self.add_gradient(
711                     clip_and_scroll,
712                     &prim_info,
713                     info.gradient.start_point,
714                     info.gradient.end_point,
715                     item.gradient_stops(),
716                     item.display_list().get(item.gradient_stops()).count(),
717                     info.gradient.extend_mode,
718                     info.tile_size,
719                     info.tile_spacing,
720                 );
721             }
722             SpecificDisplayItem::RadialGradient(ref info) => {
723                 self.add_radial_gradient(
724                     clip_and_scroll,
725                     &prim_info,
726                     info.gradient.start_center,
727                     info.gradient.start_radius,
728                     info.gradient.end_center,
729                     info.gradient.end_radius,
730                     info.gradient.ratio_xy,
731                     item.gradient_stops(),
732                     info.gradient.extend_mode,
733                     info.tile_size,
734                     info.tile_spacing,
735                 );
736             }
737             SpecificDisplayItem::BoxShadow(ref box_shadow_info) => {
738                 let bounds = box_shadow_info
739                     .box_bounds
740                     .translate(&reference_frame_relative_offset);
741                 let mut prim_info = prim_info.clone();
742                 prim_info.rect = bounds;
743                 self.add_box_shadow(
744                     clip_and_scroll,
745                     &prim_info,
746                     &box_shadow_info.offset,
747                     &box_shadow_info.color,
748                     box_shadow_info.blur_radius,
749                     box_shadow_info.spread_radius,
750                     box_shadow_info.border_radius,
751                     box_shadow_info.clip_mode,
752                 );
753             }
754             SpecificDisplayItem::Border(ref info) => {
755                 self.add_border(
756                     clip_and_scroll,
757                     &prim_info,
758                     info,
759                     item.gradient_stops(),
760                     item.display_list().get(item.gradient_stops()).count(),
761                 );
762             }
763             SpecificDisplayItem::PushStackingContext(ref info) => {
764                 let mut subtraversal = item.sub_iter();
765                 self.flatten_stacking_context(
766                     &mut subtraversal,
767                     pipeline_id,
768                     unreplaced_scroll_id,
769                     clip_and_scroll_ids.scroll_node_id,
770                     reference_frame_relative_offset,
771                     &item.rect(),
772                     &info.stacking_context,
773                     item.filters(),
774                     prim_info.is_backface_visible,
775                 );
776                 return Some(subtraversal);
777             }
778             SpecificDisplayItem::Iframe(ref info) => {
779                 self.flatten_iframe(
780                     &item,
781                     info,
782                     &clip_and_scroll_ids,
783                     &reference_frame_relative_offset
784                 );
785             }
786             SpecificDisplayItem::Clip(ref info) => {
787                 let complex_clips = self.get_complex_clips(pipeline_id, item.complex_clip().0);
788                 let clip_region = ClipRegion::create_for_clip_node(
789                     *item.local_clip().clip_rect(),
790                     complex_clips,
791                     info.image_mask,
792                     &reference_frame_relative_offset,
793                 );
794                 self.add_clip_node(info.id, clip_and_scroll_ids.scroll_node_id, clip_region);
795             }
796             SpecificDisplayItem::ClipChain(ref info) => {
797                 let items = self.get_clip_chain_items(pipeline_id, item.clip_chain_items())
798                                 .iter()
799                                 .map(|id| self.id_to_index_mapper.get_node_index(*id))
800                                 .collect();
801                 let parent = info.parent.map(|id|
802                      self.id_to_index_mapper.get_clip_chain_index(&ClipId::ClipChain(id))
803                 );
804                 let clip_chain_index =
805                     self.clip_scroll_tree.add_clip_chain_descriptor(parent, items);
806                 self.id_to_index_mapper.add_clip_chain(ClipId::ClipChain(info.id), clip_chain_index);
807             },
808             SpecificDisplayItem::ScrollFrame(ref info) => {
809                 self.flatten_scroll_frame(
810                     &item,
811                     info,
812                     pipeline_id,
813                     &clip_and_scroll_ids,
814                     &reference_frame_relative_offset
815                 );
816             }
817             SpecificDisplayItem::StickyFrame(ref info) => {
818                 self.flatten_sticky_frame(
819                     &item,
820                     info,
821                     &clip_and_scroll,
822                     &clip_and_scroll_ids.scroll_node_id,
823                     &reference_frame_relative_offset
824                 );
825             }
826 
827             // Do nothing; these are dummy items for the display list parser
828             SpecificDisplayItem::SetGradientStops => {}
829 
830             SpecificDisplayItem::PopStackingContext => {
831                 unreachable!("Should have returned in parent method.")
832             }
833             SpecificDisplayItem::PushShadow(shadow) => {
834                 let mut prim_info = prim_info.clone();
835                 prim_info.rect = LayerRect::zero();
836                 self
837                     .push_shadow(shadow, clip_and_scroll, &prim_info);
838             }
839             SpecificDisplayItem::PopAllShadows => {
840                 self.pop_all_shadows();
841             }
842         }
843         None
844     }
845 
846     /// Decomposes an image display item that is repeated into an image per individual repetition.
847     /// We need to do this when we are unable to perform the repetition in the shader,
848     /// for example if the image is tiled.
849     ///
850     /// In all of the "decompose" methods below, we independently handle horizontal and vertical
851     /// decomposition. This lets us generate the minimum amount of primitives by, for  example,
852     /// decompositing the repetition horizontally while repeating vertically in the shader (for
853     /// an image where the width is too bug but the height is not).
854     ///
855     /// decompose_image and decompose_image_row handle image repetitions while decompose_tiled_image
856     /// takes care of the decomposition required by the internal tiling of the image.
decompose_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, image_size: DeviceUintSize, tile_size: u32, )857     fn decompose_image(
858         &mut self,
859         clip_and_scroll: ScrollNodeAndClipChain,
860         prim_info: &LayerPrimitiveInfo,
861         info: &ImageDisplayItem,
862         image_size: DeviceUintSize,
863         tile_size: u32,
864     ) {
865         let no_vertical_tiling = image_size.height <= tile_size;
866         let no_vertical_spacing = info.tile_spacing.height == 0.0;
867         let item_rect = prim_info.rect;
868         if no_vertical_tiling && no_vertical_spacing {
869             self.decompose_image_row(
870                 clip_and_scroll,
871                 prim_info,
872                 info,
873                 image_size,
874                 tile_size,
875             );
876             return;
877         }
878 
879         // Decompose each vertical repetition into rows.
880         let layout_stride = info.stretch_size.height + info.tile_spacing.height;
881         let num_repetitions = (item_rect.size.height / layout_stride).ceil() as u32;
882         for i in 0 .. num_repetitions {
883             if let Some(row_rect) = rect(
884                 item_rect.origin.x,
885                 item_rect.origin.y + (i as f32) * layout_stride,
886                 item_rect.size.width,
887                 info.stretch_size.height,
888             ).intersection(&item_rect)
889             {
890                 let mut prim_info = prim_info.clone();
891                 prim_info.rect = row_rect;
892                 self.decompose_image_row(
893                     clip_and_scroll,
894                     &prim_info,
895                     info,
896                     image_size,
897                     tile_size,
898                 );
899             }
900         }
901     }
902 
decompose_image_row( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, image_size: DeviceUintSize, tile_size: u32, )903     fn decompose_image_row(
904         &mut self,
905         clip_and_scroll: ScrollNodeAndClipChain,
906         prim_info: &LayerPrimitiveInfo,
907         info: &ImageDisplayItem,
908         image_size: DeviceUintSize,
909         tile_size: u32,
910     ) {
911         let no_horizontal_tiling = image_size.width <= tile_size;
912         let no_horizontal_spacing = info.tile_spacing.width == 0.0;
913         if no_horizontal_tiling && no_horizontal_spacing {
914             self.decompose_tiled_image(
915                 clip_and_scroll,
916                 prim_info,
917                 info,
918                 image_size,
919                 tile_size,
920             );
921             return;
922         }
923 
924         // Decompose each horizontal repetition.
925         let item_rect = prim_info.rect;
926         let layout_stride = info.stretch_size.width + info.tile_spacing.width;
927         let num_repetitions = (item_rect.size.width / layout_stride).ceil() as u32;
928         for i in 0 .. num_repetitions {
929             if let Some(decomposed_rect) = rect(
930                 item_rect.origin.x + (i as f32) * layout_stride,
931                 item_rect.origin.y,
932                 info.stretch_size.width,
933                 item_rect.size.height,
934             ).intersection(&item_rect)
935             {
936                 let mut prim_info = prim_info.clone();
937                 prim_info.rect = decomposed_rect;
938                 self.decompose_tiled_image(
939                     clip_and_scroll,
940                     &prim_info,
941                     info,
942                     image_size,
943                     tile_size,
944                 );
945             }
946         }
947     }
948 
decompose_tiled_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, image_size: DeviceUintSize, tile_size: u32, )949     fn decompose_tiled_image(
950         &mut self,
951         clip_and_scroll: ScrollNodeAndClipChain,
952         prim_info: &LayerPrimitiveInfo,
953         info: &ImageDisplayItem,
954         image_size: DeviceUintSize,
955         tile_size: u32,
956     ) {
957         // The image resource is tiled. We have to generate an image primitive
958         // for each tile.
959         // We need to do this because the image is broken up into smaller tiles in the texture
960         // cache and the image shader is not able to work with this type of sparse representation.
961 
962         // The tiling logic works as follows:
963         //
964         //  ###################-+  -+
965         //  #    |    |    |//# |   | image size
966         //  #    |    |    |//# |   |
967         //  #----+----+----+--#-+   |  -+
968         //  #    |    |    |//# |   |   | regular tile size
969         //  #    |    |    |//# |   |   |
970         //  #----+----+----+--#-+   |  -+-+
971         //  #////|////|////|//# |   |     | "leftover" height
972         //  ################### |  -+  ---+
973         //  #----+----+----+----+
974         //
975         // In the ascii diagram above, a large image is plit into tiles of almost regular size.
976         // The tiles on the right and bottom edges (hatched in the diagram) are smaller than
977         // the regular tiles and are handled separately in the code see leftover_width/height.
978         // each generated image primitive corresponds to a tile in the texture cache, with the
979         // assumption that the smaller tiles with leftover sizes are sized to fit their own
980         // irregular size in the texture cache.
981         //
982         // For the case where we don't tile along an axis, we can still perform the repetition in
983         // the shader (for this particular axis), and it is worth special-casing for this to avoid
984         // generating many primitives.
985         // This can happen with very tall and thin images used as a repeating background.
986         // Apparently web authors do that...
987 
988         let item_rect = prim_info.rect;
989         let needs_repeat_x = info.stretch_size.width < item_rect.size.width;
990         let needs_repeat_y = info.stretch_size.height < item_rect.size.height;
991 
992         let tiled_in_x = image_size.width > tile_size;
993         let tiled_in_y = image_size.height > tile_size;
994 
995         // If we don't actually tile in this dimension, repeating can be done in the shader.
996         let shader_repeat_x = needs_repeat_x && !tiled_in_x;
997         let shader_repeat_y = needs_repeat_y && !tiled_in_y;
998 
999         let tile_size_f32 = tile_size as f32;
1000 
1001         // Note: this rounds down so it excludes the partially filled tiles on the right and
1002         // bottom edges (we handle them separately below).
1003         let num_tiles_x = (image_size.width / tile_size) as u16;
1004         let num_tiles_y = (image_size.height / tile_size) as u16;
1005 
1006         // Ratio between (image space) tile size and image size.
1007         let img_dw = tile_size_f32 / (image_size.width as f32);
1008         let img_dh = tile_size_f32 / (image_size.height as f32);
1009 
1010         // Strected size of the tile in layout space.
1011         let stretched_tile_size = LayerSize::new(
1012             img_dw * info.stretch_size.width,
1013             img_dh * info.stretch_size.height,
1014         );
1015 
1016         // The size in pixels of the tiles on the right and bottom edges, smaller
1017         // than the regular tile size if the image is not a multiple of the tile size.
1018         // Zero means the image size is a multiple of the tile size.
1019         let leftover =
1020             DeviceUintSize::new(image_size.width % tile_size, image_size.height % tile_size);
1021 
1022         for ty in 0 .. num_tiles_y {
1023             for tx in 0 .. num_tiles_x {
1024                 self.add_tile_primitive(
1025                     clip_and_scroll,
1026                     prim_info,
1027                     info,
1028                     TileOffset::new(tx, ty),
1029                     stretched_tile_size,
1030                     1.0,
1031                     1.0,
1032                     shader_repeat_x,
1033                     shader_repeat_y,
1034                 );
1035             }
1036             if leftover.width != 0 {
1037                 // Tiles on the right edge that are smaller than the tile size.
1038                 self.add_tile_primitive(
1039                     clip_and_scroll,
1040                     prim_info,
1041                     info,
1042                     TileOffset::new(num_tiles_x, ty),
1043                     stretched_tile_size,
1044                     (leftover.width as f32) / tile_size_f32,
1045                     1.0,
1046                     shader_repeat_x,
1047                     shader_repeat_y,
1048                 );
1049             }
1050         }
1051 
1052         if leftover.height != 0 {
1053             for tx in 0 .. num_tiles_x {
1054                 // Tiles on the bottom edge that are smaller than the tile size.
1055                 self.add_tile_primitive(
1056                     clip_and_scroll,
1057                     prim_info,
1058                     info,
1059                     TileOffset::new(tx, num_tiles_y),
1060                     stretched_tile_size,
1061                     1.0,
1062                     (leftover.height as f32) / tile_size_f32,
1063                     shader_repeat_x,
1064                     shader_repeat_y,
1065                 );
1066             }
1067 
1068             if leftover.width != 0 {
1069                 // Finally, the bottom-right tile with a "leftover" size.
1070                 self.add_tile_primitive(
1071                     clip_and_scroll,
1072                     prim_info,
1073                     info,
1074                     TileOffset::new(num_tiles_x, num_tiles_y),
1075                     stretched_tile_size,
1076                     (leftover.width as f32) / tile_size_f32,
1077                     (leftover.height as f32) / tile_size_f32,
1078                     shader_repeat_x,
1079                     shader_repeat_y,
1080                 );
1081             }
1082         }
1083     }
1084 
add_tile_primitive( &mut self, clip_and_scroll: ScrollNodeAndClipChain, prim_info: &LayerPrimitiveInfo, info: &ImageDisplayItem, tile_offset: TileOffset, stretched_tile_size: LayerSize, tile_ratio_width: f32, tile_ratio_height: f32, shader_repeat_x: bool, shader_repeat_y: bool, )1085     fn add_tile_primitive(
1086         &mut self,
1087         clip_and_scroll: ScrollNodeAndClipChain,
1088         prim_info: &LayerPrimitiveInfo,
1089         info: &ImageDisplayItem,
1090         tile_offset: TileOffset,
1091         stretched_tile_size: LayerSize,
1092         tile_ratio_width: f32,
1093         tile_ratio_height: f32,
1094         shader_repeat_x: bool,
1095         shader_repeat_y: bool,
1096     ) {
1097         // If the the image is tiled along a given axis, we can't have the shader compute
1098         // the image repetition pattern. In this case we base the primitive's rectangle size
1099         // on the stretched tile size which effectively cancels the repetion (and repetition
1100         // has to be emulated by generating more primitives).
1101         // If the image is not tiled along this axis, we can perform the repetition in the
1102         // shader. in this case we use the item's size in the primitive (on that particular
1103         // axis).
1104         // See the shader_repeat_x/y code below.
1105 
1106         let stretched_size = LayerSize::new(
1107             stretched_tile_size.width * tile_ratio_width,
1108             stretched_tile_size.height * tile_ratio_height,
1109         );
1110 
1111         let mut prim_rect = LayerRect::new(
1112             prim_info.rect.origin +
1113                 LayerVector2D::new(
1114                     tile_offset.x as f32 * stretched_tile_size.width,
1115                     tile_offset.y as f32 * stretched_tile_size.height,
1116                 ),
1117             stretched_size,
1118         );
1119 
1120         if shader_repeat_x {
1121             assert_eq!(tile_offset.x, 0);
1122             prim_rect.size.width = prim_info.rect.size.width;
1123         }
1124 
1125         if shader_repeat_y {
1126             assert_eq!(tile_offset.y, 0);
1127             prim_rect.size.height = prim_info.rect.size.height;
1128         }
1129 
1130         // Fix up the primitive's rect if it overflows the original item rect.
1131         if let Some(prim_rect) = prim_rect.intersection(&prim_info.rect) {
1132             let mut prim_info = prim_info.clone();
1133             prim_info.rect = prim_rect;
1134             self.add_image(
1135                 clip_and_scroll,
1136                 &prim_info,
1137                 stretched_size,
1138                 info.tile_spacing,
1139                 None,
1140                 info.image_key,
1141                 info.image_rendering,
1142                 info.alpha_type,
1143                 Some(tile_offset),
1144             );
1145         }
1146     }
1147 
1148     /// Create a primitive and add it to the prim store. This method doesn't
1149     /// add the primitive to the draw list, so can be used for creating
1150     /// sub-primitives.
create_primitive( &mut self, info: &LayerPrimitiveInfo, mut clip_sources: Vec<ClipSource>, container: PrimitiveContainer, ) -> PrimitiveIndex1151     pub fn create_primitive(
1152         &mut self,
1153         info: &LayerPrimitiveInfo,
1154         mut clip_sources: Vec<ClipSource>,
1155         container: PrimitiveContainer,
1156     ) -> PrimitiveIndex {
1157         if let &LocalClip::RoundedRect(main, region) = &info.local_clip {
1158             clip_sources.push(ClipSource::Rectangle(main));
1159 
1160             clip_sources.push(ClipSource::new_rounded_rect(
1161                 region.rect,
1162                 region.radii,
1163                 region.mode,
1164             ));
1165         }
1166 
1167         let stacking_context = self.sc_stack.last().expect("bug: no stacking context!");
1168 
1169         let clip_sources = if clip_sources.is_empty() {
1170             None
1171         } else {
1172             Some(self.clip_store.insert(ClipSources::new(clip_sources)))
1173         };
1174 
1175         let prim_index = self.prim_store.add_primitive(
1176             &info.rect,
1177             &info.local_clip.clip_rect(),
1178             info.is_backface_visible && stacking_context.is_backface_visible,
1179             clip_sources,
1180             info.tag,
1181             container,
1182         );
1183 
1184         prim_index
1185     }
1186 
add_primitive_to_hit_testing_list( &mut self, info: &LayerPrimitiveInfo, clip_and_scroll: ScrollNodeAndClipChain )1187     pub fn add_primitive_to_hit_testing_list(
1188         &mut self,
1189         info: &LayerPrimitiveInfo,
1190         clip_and_scroll: ScrollNodeAndClipChain
1191     ) {
1192         let tag = match info.tag {
1193             Some(tag) => tag,
1194             None => return,
1195         };
1196 
1197         let new_item = HitTestingItem::new(tag, info);
1198         match self.hit_testing_runs.last_mut() {
1199             Some(&mut HitTestingRun(ref mut items, prev_clip_and_scroll))
1200                 if prev_clip_and_scroll == clip_and_scroll => {
1201                 items.push(new_item);
1202                 return;
1203             }
1204             _ => {}
1205         }
1206 
1207         self.hit_testing_runs.push(HitTestingRun(vec![new_item], clip_and_scroll));
1208     }
1209 
1210     /// Add an already created primitive to the draw lists.
add_primitive_to_draw_list( &mut self, prim_index: PrimitiveIndex, clip_and_scroll: ScrollNodeAndClipChain, )1211     pub fn add_primitive_to_draw_list(
1212         &mut self,
1213         prim_index: PrimitiveIndex,
1214         clip_and_scroll: ScrollNodeAndClipChain,
1215     ) {
1216         // Add primitive to the top-most Picture on the stack.
1217         // TODO(gw): Let's consider removing the extra indirection
1218         //           needed to get a specific primitive index...
1219         let pic_prim_index = self.picture_stack.last().unwrap();
1220         let metadata = &self.prim_store.cpu_metadata[pic_prim_index.0];
1221         let pic = &mut self.prim_store.cpu_pictures[metadata.cpu_prim_index.0];
1222         pic.add_primitive(
1223             prim_index,
1224             clip_and_scroll
1225         );
1226     }
1227 
1228     /// Convenience interface that creates a primitive entry and adds it
1229     /// to the draw list.
add_primitive( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, clip_sources: Vec<ClipSource>, container: PrimitiveContainer, ) -> PrimitiveIndex1230     pub fn add_primitive(
1231         &mut self,
1232         clip_and_scroll: ScrollNodeAndClipChain,
1233         info: &LayerPrimitiveInfo,
1234         clip_sources: Vec<ClipSource>,
1235         container: PrimitiveContainer,
1236     ) -> PrimitiveIndex {
1237         self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
1238         let prim_index = self.create_primitive(info, clip_sources, container);
1239 
1240         self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1241         prim_index
1242     }
1243 
push_stacking_context( &mut self, pipeline_id: PipelineId, composite_ops: CompositeOps, transform_style: TransformStyle, is_backface_visible: bool, is_pipeline_root: bool, clip_and_scroll: ScrollNodeAndClipChain, )1244     pub fn push_stacking_context(
1245         &mut self,
1246         pipeline_id: PipelineId,
1247         composite_ops: CompositeOps,
1248         transform_style: TransformStyle,
1249         is_backface_visible: bool,
1250         is_pipeline_root: bool,
1251         clip_and_scroll: ScrollNodeAndClipChain,
1252     ) {
1253         // Construct the necessary set of Picture primitives
1254         // to draw this stacking context.
1255         let current_reference_frame_index = self.current_reference_frame_index();
1256 
1257         // An arbitrary large clip rect. For now, we don't
1258         // specify a clip specific to the stacking context.
1259         // However, now that they are represented as Picture
1260         // primitives, we can apply any kind of clip mask
1261         // to them, as for a normal primitive. This is needed
1262         // to correctly handle some CSS cases (see #1957).
1263         let max_clip = LayerRect::max_rect();
1264 
1265         // If there is no root picture, create one for the main framebuffer.
1266         if self.sc_stack.is_empty() {
1267             // Should be no pictures at all if the stack is empty...
1268             debug_assert!(self.prim_store.cpu_pictures.is_empty());
1269             debug_assert_eq!(transform_style, TransformStyle::Flat);
1270 
1271             // This picture stores primitive runs for items on the
1272             // main framebuffer.
1273             let pic = PicturePrimitive::new_image(
1274                 None,
1275                 false,
1276                 pipeline_id,
1277                 current_reference_frame_index,
1278                 None,
1279             );
1280 
1281             // Add root picture primitive. The provided layer rect
1282             // is zero, because we don't yet know the size of the
1283             // picture. Instead, this is calculated recursively
1284             // when we cull primitives.
1285             let prim_index = self.prim_store.add_primitive(
1286                 &LayerRect::zero(),
1287                 &max_clip,
1288                 true,
1289                 None,
1290                 None,
1291                 PrimitiveContainer::Picture(pic),
1292             );
1293 
1294             self.picture_stack.push(prim_index);
1295         } else if composite_ops.mix_blend_mode.is_some() && self.sc_stack.len() > 2 {
1296             // If we have a mix-blend-mode, and we aren't the primary framebuffer,
1297             // the stacking context needs to be isolated to blend correctly as per
1298             // the CSS spec.
1299             // TODO(gw): The way we detect not being the primary framebuffer (len > 2)
1300             //           is hacky and depends on how we create a root stacking context
1301             //           during flattening.
1302             let current_pic_prim_index = self.picture_stack.last().unwrap();
1303             let pic_cpu_prim_index = self.prim_store.cpu_metadata[current_pic_prim_index.0].cpu_prim_index;
1304             let parent_pic = &mut self.prim_store.cpu_pictures[pic_cpu_prim_index.0];
1305 
1306             match parent_pic.kind {
1307                 PictureKind::Image { ref mut composite_mode, .. } => {
1308                     // If not already isolated for some other reason,
1309                     // make this picture as isolated.
1310                     if composite_mode.is_none() {
1311                         *composite_mode = Some(PictureCompositeMode::Blit);
1312                     }
1313                 }
1314                 PictureKind::TextShadow { .. } => {
1315                     panic!("bug: text pictures invalid here");
1316                 }
1317             }
1318         }
1319 
1320         // Get the transform-style of the parent stacking context,
1321         // which determines if we *might* need to draw this on
1322         // an intermediate surface for plane splitting purposes.
1323         let parent_transform_style = match self.sc_stack.last() {
1324             Some(sc) => sc.transform_style,
1325             None => TransformStyle::Flat,
1326         };
1327 
1328         // If this is preserve-3d *or* the parent is, then this stacking
1329         // context is participating in the 3d rendering context. In that
1330         // case, hoist the picture up to the 3d rendering context
1331         // container, so that it's rendered as a sibling with other
1332         // elements in this context.
1333         let participating_in_3d_context =
1334             composite_ops.count() == 0 &&
1335             (parent_transform_style == TransformStyle::Preserve3D ||
1336              transform_style == TransformStyle::Preserve3D);
1337 
1338         // If this is participating in a 3d context *and* the
1339         // parent was not a 3d context, then this must be the
1340         // element that establishes a new 3d context.
1341         let establishes_3d_context =
1342             participating_in_3d_context &&
1343             parent_transform_style == TransformStyle::Flat;
1344 
1345         let rendering_context_3d_prim_index = if establishes_3d_context {
1346             // If establishing a 3d context, we need to add a picture
1347             // that will be the container for all the planes and any
1348             // un-transformed content.
1349             let container = PicturePrimitive::new_image(
1350                 None,
1351                 false,
1352                 pipeline_id,
1353                 current_reference_frame_index,
1354                 None,
1355             );
1356 
1357             let prim_index = self.prim_store.add_primitive(
1358                 &LayerRect::zero(),
1359                 &max_clip,
1360                 is_backface_visible,
1361                 None,
1362                 None,
1363                 PrimitiveContainer::Picture(container),
1364             );
1365 
1366             let parent_pic_prim_index = *self.picture_stack.last().unwrap();
1367             let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1368             let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1369             pic.add_primitive(
1370                 prim_index,
1371                 clip_and_scroll,
1372             );
1373 
1374             self.picture_stack.push(prim_index);
1375 
1376             Some(prim_index)
1377         } else {
1378             None
1379         };
1380 
1381         let mut parent_pic_prim_index = if !establishes_3d_context && participating_in_3d_context {
1382             // If we're in a 3D context, we will parent the picture
1383             // to the first stacking context we find that is a
1384             // 3D rendering context container. This follows the spec
1385             // by hoisting these items out into the same 3D context
1386             // for plane splitting.
1387             self.sc_stack
1388                 .iter()
1389                 .rev()
1390                 .find(|sc| sc.rendering_context_3d_prim_index.is_some())
1391                 .map(|sc| sc.rendering_context_3d_prim_index.unwrap())
1392                 .unwrap()
1393         } else {
1394             *self.picture_stack.last().unwrap()
1395         };
1396 
1397         // For each filter, create a new image with that composite mode.
1398         for filter in composite_ops.filters.iter().rev() {
1399             let src_prim = PicturePrimitive::new_image(
1400                 Some(PictureCompositeMode::Filter(*filter)),
1401                 false,
1402                 pipeline_id,
1403                 current_reference_frame_index,
1404                 None,
1405             );
1406 
1407             let src_prim_index = self.prim_store.add_primitive(
1408                 &LayerRect::zero(),
1409                 &max_clip,
1410                 is_backface_visible,
1411                 None,
1412                 None,
1413                 PrimitiveContainer::Picture(src_prim),
1414             );
1415 
1416             let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1417             parent_pic_prim_index = src_prim_index;
1418             let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1419             pic.add_primitive(
1420                 src_prim_index,
1421                 clip_and_scroll,
1422             );
1423 
1424             self.picture_stack.push(src_prim_index);
1425         }
1426 
1427         // Same for mix-blend-mode.
1428         if let Some(mix_blend_mode) = composite_ops.mix_blend_mode {
1429             let src_prim = PicturePrimitive::new_image(
1430                 Some(PictureCompositeMode::MixBlend(mix_blend_mode)),
1431                 false,
1432                 pipeline_id,
1433                 current_reference_frame_index,
1434                 None,
1435             );
1436 
1437             let src_prim_index = self.prim_store.add_primitive(
1438                 &LayerRect::zero(),
1439                 &max_clip,
1440                 is_backface_visible,
1441                 None,
1442                 None,
1443                 PrimitiveContainer::Picture(src_prim),
1444             );
1445 
1446             let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1447             parent_pic_prim_index = src_prim_index;
1448             let pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1449             pic.add_primitive(
1450                 src_prim_index,
1451                 clip_and_scroll,
1452             );
1453 
1454             self.picture_stack.push(src_prim_index);
1455         }
1456 
1457         // By default, this picture will be collapsed into
1458         // the owning target.
1459         let mut composite_mode = None;
1460         let mut frame_output_pipeline_id = None;
1461 
1462         // If this stacking context if the root of a pipeline, and the caller
1463         // has requested it as an output frame, create a render task to isolate it.
1464         if is_pipeline_root && self.output_pipelines.contains(&pipeline_id) {
1465             composite_mode = Some(PictureCompositeMode::Blit);
1466             frame_output_pipeline_id = Some(pipeline_id);
1467         }
1468 
1469         if participating_in_3d_context {
1470             // TODO(gw): For now, as soon as this picture is in
1471             //           a 3D context, we draw it to an intermediate
1472             //           surface and apply plane splitting. However,
1473             //           there is a large optimization opportunity here.
1474             //           During culling, we can check if there is actually
1475             //           perspective present, and skip the plane splitting
1476             //           completely when that is not the case.
1477             composite_mode = Some(PictureCompositeMode::Blit);
1478         }
1479 
1480         // Add picture for this actual stacking context contents to render into.
1481         let sc_prim = PicturePrimitive::new_image(
1482             composite_mode,
1483             participating_in_3d_context,
1484             pipeline_id,
1485             current_reference_frame_index,
1486             frame_output_pipeline_id,
1487         );
1488 
1489         let sc_prim_index = self.prim_store.add_primitive(
1490             &LayerRect::zero(),
1491             &max_clip,
1492             is_backface_visible,
1493             None,
1494             None,
1495             PrimitiveContainer::Picture(sc_prim),
1496         );
1497 
1498         let pic_prim_index = self.prim_store.cpu_metadata[parent_pic_prim_index.0].cpu_prim_index;
1499         let sc_pic = &mut self.prim_store.cpu_pictures[pic_prim_index.0];
1500         sc_pic.add_primitive(
1501             sc_prim_index,
1502             clip_and_scroll,
1503         );
1504 
1505         // Add this as the top-most picture for primitives to be added to.
1506         self.picture_stack.push(sc_prim_index);
1507 
1508         // TODO(gw): This is super conservative. We can expand on this a lot
1509         //           once all the picture code is in place and landed.
1510         let allow_subpixel_aa = composite_ops.count() == 0 &&
1511                                 transform_style == TransformStyle::Flat;
1512 
1513         // Push the SC onto the stack, so we know how to handle things in
1514         // pop_stacking_context.
1515         let sc = FlattenedStackingContext {
1516             composite_ops,
1517             is_backface_visible,
1518             pipeline_id,
1519             allow_subpixel_aa,
1520             transform_style,
1521             rendering_context_3d_prim_index,
1522         };
1523 
1524         self.sc_stack.push(sc);
1525     }
1526 
pop_stacking_context(&mut self)1527     pub fn pop_stacking_context(&mut self) {
1528         let sc = self.sc_stack.pop().unwrap();
1529 
1530         // Always pop at least the main picture for this stacking context.
1531         let mut pop_count = 1;
1532 
1533         // Remove the picture for any filter/mix-blend-mode effects.
1534         pop_count += sc.composite_ops.count();
1535 
1536         // Remove the 3d context container if created
1537         if sc.rendering_context_3d_prim_index.is_some() {
1538             pop_count += 1;
1539         }
1540 
1541         for _ in 0 .. pop_count {
1542             self.picture_stack.pop().expect("bug: mismatched picture stack");
1543         }
1544 
1545         // By the time the stacking context stack is empty, we should
1546         // also have cleared the picture stack.
1547         if self.sc_stack.is_empty() {
1548             self.picture_stack.pop().expect("bug: picture stack invalid");
1549             debug_assert!(self.picture_stack.is_empty());
1550         }
1551 
1552         assert!(
1553             self.shadow_prim_stack.is_empty(),
1554             "Found unpopped text shadows when popping stacking context!"
1555         );
1556     }
1557 
push_reference_frame( &mut self, reference_frame_id: ClipId, parent_id: Option<ClipId>, pipeline_id: PipelineId, rect: &LayerRect, source_transform: Option<PropertyBinding<LayoutTransform>>, source_perspective: Option<LayoutTransform>, origin_in_parent_reference_frame: LayerVector2D, ) -> ClipScrollNodeIndex1558     pub fn push_reference_frame(
1559         &mut self,
1560         reference_frame_id: ClipId,
1561         parent_id: Option<ClipId>,
1562         pipeline_id: PipelineId,
1563         rect: &LayerRect,
1564         source_transform: Option<PropertyBinding<LayoutTransform>>,
1565         source_perspective: Option<LayoutTransform>,
1566         origin_in_parent_reference_frame: LayerVector2D,
1567     ) -> ClipScrollNodeIndex {
1568         let index = self.id_to_index_mapper.get_node_index(reference_frame_id);
1569         let node = ClipScrollNode::new_reference_frame(
1570             parent_id.map(|id| self.id_to_index_mapper.get_node_index(id)),
1571             rect,
1572             source_transform,
1573             source_perspective,
1574             origin_in_parent_reference_frame,
1575             pipeline_id,
1576         );
1577         self.clip_scroll_tree.add_node(node, index);
1578         self.reference_frame_stack.push((reference_frame_id, index));
1579 
1580         match parent_id {
1581             Some(ref parent_id) =>
1582                 self.id_to_index_mapper.map_to_parent_clip_chain(reference_frame_id, parent_id),
1583             _ => self.id_to_index_mapper.add_clip_chain(reference_frame_id, ClipChainIndex(0)),
1584         }
1585         index
1586     }
1587 
current_reference_frame_index(&self) -> ClipScrollNodeIndex1588     pub fn current_reference_frame_index(&self) -> ClipScrollNodeIndex {
1589         self.reference_frame_stack.last().unwrap().1
1590     }
1591 
current_reference_frame_id(&self) -> ClipId1592     pub fn current_reference_frame_id(&self) -> ClipId{
1593         self.reference_frame_stack.last().unwrap().0
1594     }
1595 
setup_viewport_offset( &mut self, inner_rect: DeviceUintRect, device_pixel_scale: DevicePixelScale, )1596     pub fn setup_viewport_offset(
1597         &mut self,
1598         inner_rect: DeviceUintRect,
1599         device_pixel_scale: DevicePixelScale,
1600     ) {
1601         let viewport_offset = (inner_rect.origin.to_vector().to_f32() / device_pixel_scale).round();
1602         let root_id = self.clip_scroll_tree.root_reference_frame_index();
1603         let root_node = &mut self.clip_scroll_tree.nodes[root_id.0];
1604         if let NodeType::ReferenceFrame(ref mut info) = root_node.node_type {
1605             info.resolved_transform =
1606                 LayerVector2D::new(viewport_offset.x, viewport_offset.y).into();
1607         }
1608     }
1609 
push_root( &mut self, pipeline_id: PipelineId, viewport_size: &LayerSize, content_size: &LayerSize, )1610     pub fn push_root(
1611         &mut self,
1612         pipeline_id: PipelineId,
1613         viewport_size: &LayerSize,
1614         content_size: &LayerSize,
1615     ) {
1616         let viewport_rect = LayerRect::new(LayerPoint::zero(), *viewport_size);
1617 
1618         self.push_reference_frame(
1619             ClipId::root_reference_frame(pipeline_id),
1620             None,
1621             pipeline_id,
1622             &viewport_rect,
1623             None,
1624             None,
1625             LayerVector2D::zero(),
1626         );
1627 
1628         self.add_scroll_frame(
1629             ClipId::root_scroll_node(pipeline_id),
1630             ClipId::root_reference_frame(pipeline_id),
1631             Some(ExternalScrollId(0, pipeline_id)),
1632             pipeline_id,
1633             &viewport_rect,
1634             content_size,
1635             ScrollSensitivity::ScriptAndInputEvents,
1636         );
1637     }
1638 
add_clip_node( &mut self, new_node_id: ClipId, parent_id: ClipId, clip_region: ClipRegion, ) -> ClipScrollNodeIndex1639     pub fn add_clip_node(
1640         &mut self,
1641         new_node_id: ClipId,
1642         parent_id: ClipId,
1643         clip_region: ClipRegion,
1644     ) -> ClipScrollNodeIndex {
1645         let clip_rect = clip_region.main;
1646         let clip_sources = ClipSources::from(clip_region);
1647         let handle = self.clip_store.insert(clip_sources);
1648 
1649         let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
1650         let clip_chain_index = self.clip_scroll_tree.add_clip_node(
1651             node_index,
1652             self.id_to_index_mapper.get_node_index(parent_id),
1653             handle,
1654             clip_rect,
1655             new_node_id.pipeline_id(),
1656         );
1657         self.id_to_index_mapper.add_clip_chain(new_node_id, clip_chain_index);
1658         node_index
1659     }
1660 
add_scroll_frame( &mut self, new_node_id: ClipId, parent_id: ClipId, external_id: Option<ExternalScrollId>, pipeline_id: PipelineId, frame_rect: &LayerRect, content_size: &LayerSize, scroll_sensitivity: ScrollSensitivity, ) -> ClipScrollNodeIndex1661     pub fn add_scroll_frame(
1662         &mut self,
1663         new_node_id: ClipId,
1664         parent_id: ClipId,
1665         external_id: Option<ExternalScrollId>,
1666         pipeline_id: PipelineId,
1667         frame_rect: &LayerRect,
1668         content_size: &LayerSize,
1669         scroll_sensitivity: ScrollSensitivity,
1670     ) -> ClipScrollNodeIndex {
1671         let node_index = self.id_to_index_mapper.get_node_index(new_node_id);
1672         let node = ClipScrollNode::new_scroll_frame(
1673             pipeline_id,
1674             self.id_to_index_mapper.get_node_index(parent_id),
1675             external_id,
1676             frame_rect,
1677             content_size,
1678             scroll_sensitivity,
1679         );
1680 
1681         self.clip_scroll_tree.add_node(node, node_index);
1682         self.id_to_index_mapper.map_to_parent_clip_chain(new_node_id, &parent_id);
1683         node_index
1684     }
1685 
pop_reference_frame(&mut self)1686     pub fn pop_reference_frame(&mut self) {
1687         self.reference_frame_stack.pop();
1688     }
1689 
push_shadow( &mut self, shadow: Shadow, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, )1690     pub fn push_shadow(
1691         &mut self,
1692         shadow: Shadow,
1693         clip_and_scroll: ScrollNodeAndClipChain,
1694         info: &LayerPrimitiveInfo,
1695     ) {
1696         let pipeline_id = self.sc_stack.last().unwrap().pipeline_id;
1697         let prim = PicturePrimitive::new_text_shadow(shadow, pipeline_id);
1698 
1699         // Create an empty shadow primitive. Insert it into
1700         // the draw lists immediately so that it will be drawn
1701         // before any visual text elements that are added as
1702         // part of this shadow context.
1703         let prim_index = self.create_primitive(
1704             info,
1705             Vec::new(),
1706             PrimitiveContainer::Picture(prim),
1707         );
1708 
1709         let pending = vec![(prim_index, clip_and_scroll)];
1710         self.shadow_prim_stack.push((prim_index, pending));
1711     }
1712 
pop_all_shadows(&mut self)1713     pub fn pop_all_shadows(&mut self) {
1714         assert!(self.shadow_prim_stack.len() > 0, "popped shadows, but none were present");
1715 
1716         // Borrowcheck dance
1717         let mut shadows = mem::replace(&mut self.shadow_prim_stack, Vec::new());
1718         for (_, pending_primitives) in shadows.drain(..) {
1719             // Push any fast-path shadows now
1720             for (prim_index, clip_and_scroll) in pending_primitives {
1721                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1722             }
1723         }
1724 
1725         let mut pending_primitives = mem::replace(&mut self.pending_shadow_contents, Vec::new());
1726         for (prim_index, clip_and_scroll, info) in pending_primitives.drain(..) {
1727             self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
1728             self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1729         }
1730 
1731         mem::replace(&mut self.pending_shadow_contents, pending_primitives);
1732         mem::replace(&mut self.shadow_prim_stack, shadows);
1733     }
1734 
add_solid_rectangle( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, color: ColorF, segments: Option<BrushSegmentDescriptor>, )1735     pub fn add_solid_rectangle(
1736         &mut self,
1737         clip_and_scroll: ScrollNodeAndClipChain,
1738         info: &LayerPrimitiveInfo,
1739         color: ColorF,
1740         segments: Option<BrushSegmentDescriptor>,
1741     ) {
1742         if color.a == 0.0 {
1743             // Don't add transparent rectangles to the draw list, but do consider them for hit
1744             // testing. This allows specifying invisible hit testing areas.
1745             self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
1746             return;
1747         }
1748 
1749         let prim = BrushPrimitive::new(
1750             BrushKind::Solid {
1751                 color,
1752             },
1753             segments,
1754         );
1755 
1756         self.add_primitive(
1757             clip_and_scroll,
1758             info,
1759             Vec::new(),
1760             PrimitiveContainer::Brush(prim),
1761         );
1762     }
1763 
add_clear_rectangle( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, )1764     pub fn add_clear_rectangle(
1765         &mut self,
1766         clip_and_scroll: ScrollNodeAndClipChain,
1767         info: &LayerPrimitiveInfo,
1768     ) {
1769         let prim = BrushPrimitive::new(
1770             BrushKind::Clear,
1771             None,
1772         );
1773 
1774         self.add_primitive(
1775             clip_and_scroll,
1776             info,
1777             Vec::new(),
1778             PrimitiveContainer::Brush(prim),
1779         );
1780     }
1781 
add_scroll_bar( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, color: ColorF, scrollbar_info: ScrollbarInfo, )1782     pub fn add_scroll_bar(
1783         &mut self,
1784         clip_and_scroll: ScrollNodeAndClipChain,
1785         info: &LayerPrimitiveInfo,
1786         color: ColorF,
1787         scrollbar_info: ScrollbarInfo,
1788     ) {
1789         if color.a == 0.0 {
1790             return;
1791         }
1792 
1793         let prim = BrushPrimitive::new(
1794             BrushKind::Solid {
1795                 color,
1796             },
1797             None,
1798         );
1799 
1800         let prim_index = self.add_primitive(
1801             clip_and_scroll,
1802             info,
1803             Vec::new(),
1804             PrimitiveContainer::Brush(prim),
1805         );
1806 
1807         self.scrollbar_prims.push(ScrollbarPrimitive {
1808             prim_index,
1809             scroll_frame_index: scrollbar_info.0,
1810             frame_rect: scrollbar_info.1,
1811         });
1812     }
1813 
add_line( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, wavy_line_thickness: f32, orientation: LineOrientation, line_color: &ColorF, style: LineStyle, )1814     pub fn add_line(
1815         &mut self,
1816         clip_and_scroll: ScrollNodeAndClipChain,
1817         info: &LayerPrimitiveInfo,
1818         wavy_line_thickness: f32,
1819         orientation: LineOrientation,
1820         line_color: &ColorF,
1821         style: LineStyle,
1822     ) {
1823         let line = BrushPrimitive::new(
1824             BrushKind::Line {
1825                 wavy_line_thickness,
1826                 color: line_color.premultiplied(),
1827                 style,
1828                 orientation,
1829             },
1830             None,
1831         );
1832 
1833         let mut fast_shadow_prims = Vec::new();
1834         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
1835             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
1836             let picture = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
1837             match picture.kind {
1838                 PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
1839                     fast_shadow_prims.push((idx, offset, color));
1840                 }
1841                 _ => {}
1842             }
1843         }
1844 
1845         for (idx, shadow_offset, shadow_color) in fast_shadow_prims {
1846             let line = BrushPrimitive::new(
1847                 BrushKind::Line {
1848                     wavy_line_thickness,
1849                     color: shadow_color.premultiplied(),
1850                     style,
1851                     orientation,
1852                 },
1853                 None,
1854             );
1855             let mut info = info.clone();
1856             info.rect = info.rect.translate(&shadow_offset);
1857             info.local_clip =
1858               LocalClip::from(info.local_clip.clip_rect().translate(&shadow_offset));
1859             let prim_index = self.create_primitive(
1860                 &info,
1861                 Vec::new(),
1862                 PrimitiveContainer::Brush(line),
1863             );
1864             self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
1865         }
1866 
1867         let prim_index = self.create_primitive(
1868             &info,
1869             Vec::new(),
1870             PrimitiveContainer::Brush(line),
1871         );
1872 
1873         if line_color.a > 0.0 {
1874             if self.shadow_prim_stack.is_empty() {
1875                 self.add_primitive_to_hit_testing_list(&info, clip_and_scroll);
1876                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
1877             } else {
1878                 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
1879             }
1880         }
1881 
1882         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
1883             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
1884             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
1885             let picture =
1886                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
1887 
1888             match picture.kind {
1889                 // Only run real blurs here (fast path zero blurs are handled above).
1890                 PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
1891                     picture.add_primitive(
1892                         prim_index,
1893                         clip_and_scroll,
1894                     );
1895                 }
1896                 _ => {}
1897             }
1898         }
1899     }
1900 
add_border( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, border_item: &BorderDisplayItem, gradient_stops: ItemRange<GradientStop>, gradient_stops_count: usize, )1901     pub fn add_border(
1902         &mut self,
1903         clip_and_scroll: ScrollNodeAndClipChain,
1904         info: &LayerPrimitiveInfo,
1905         border_item: &BorderDisplayItem,
1906         gradient_stops: ItemRange<GradientStop>,
1907         gradient_stops_count: usize,
1908     ) {
1909         let rect = info.rect;
1910         let create_segments = |outset: SideOffsets2D<f32>| {
1911             // Calculate the modified rect as specific by border-image-outset
1912             let origin = LayerPoint::new(rect.origin.x - outset.left, rect.origin.y - outset.top);
1913             let size = LayerSize::new(
1914                 rect.size.width + outset.left + outset.right,
1915                 rect.size.height + outset.top + outset.bottom,
1916             );
1917             let rect = LayerRect::new(origin, size);
1918 
1919             let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
1920             let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
1921 
1922             let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
1923             let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
1924 
1925             let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
1926             let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
1927 
1928             let br_outer = LayerPoint::new(
1929                 rect.origin.x + rect.size.width,
1930                 rect.origin.y + rect.size.height,
1931             );
1932             let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
1933 
1934             // Build the list of gradient segments
1935             vec![
1936                 // Top left
1937                 LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
1938                 // Top right
1939                 LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
1940                 // Bottom right
1941                 LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
1942                 // Bottom left
1943                 LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
1944                 // Top
1945                 LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
1946                 // Bottom
1947                 LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
1948                 // Left
1949                 LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
1950                 // Right
1951                 LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
1952             ]
1953         };
1954 
1955         match border_item.details {
1956             BorderDetails::Image(ref border) => {
1957                 // Calculate the modified rect as specific by border-image-outset
1958                 let origin = LayerPoint::new(
1959                     rect.origin.x - border.outset.left,
1960                     rect.origin.y - border.outset.top,
1961                 );
1962                 let size = LayerSize::new(
1963                     rect.size.width + border.outset.left + border.outset.right,
1964                     rect.size.height + border.outset.top + border.outset.bottom,
1965                 );
1966                 let rect = LayerRect::new(origin, size);
1967 
1968                 // Calculate the local texel coords of the slices.
1969                 let px0 = 0.0;
1970                 let px1 = border.patch.slice.left as f32;
1971                 let px2 = border.patch.width as f32 - border.patch.slice.right as f32;
1972                 let px3 = border.patch.width as f32;
1973 
1974                 let py0 = 0.0;
1975                 let py1 = border.patch.slice.top as f32;
1976                 let py2 = border.patch.height as f32 - border.patch.slice.bottom as f32;
1977                 let py3 = border.patch.height as f32;
1978 
1979                 let tl_outer = LayerPoint::new(rect.origin.x, rect.origin.y);
1980                 let tl_inner = tl_outer + vec2(border_item.widths.left, border_item.widths.top);
1981 
1982                 let tr_outer = LayerPoint::new(rect.origin.x + rect.size.width, rect.origin.y);
1983                 let tr_inner = tr_outer + vec2(-border_item.widths.right, border_item.widths.top);
1984 
1985                 let bl_outer = LayerPoint::new(rect.origin.x, rect.origin.y + rect.size.height);
1986                 let bl_inner = bl_outer + vec2(border_item.widths.left, -border_item.widths.bottom);
1987 
1988                 let br_outer = LayerPoint::new(
1989                     rect.origin.x + rect.size.width,
1990                     rect.origin.y + rect.size.height,
1991                 );
1992                 let br_inner = br_outer - vec2(border_item.widths.right, border_item.widths.bottom);
1993 
1994                 fn add_segment(
1995                     segments: &mut Vec<ImageBorderSegment>,
1996                     rect: LayerRect,
1997                     uv_rect: TexelRect,
1998                     repeat_horizontal: RepeatMode,
1999                     repeat_vertical: RepeatMode) {
2000                     if uv_rect.uv1.x > uv_rect.uv0.x &&
2001                        uv_rect.uv1.y > uv_rect.uv0.y {
2002                         segments.push(ImageBorderSegment::new(
2003                             rect,
2004                             uv_rect,
2005                             repeat_horizontal,
2006                             repeat_vertical,
2007                         ));
2008                     }
2009                 }
2010 
2011                 // Build the list of image segments
2012                 let mut segments = vec![];
2013 
2014                 // Top left
2015                 add_segment(
2016                     &mut segments,
2017                     LayerRect::from_floats(tl_outer.x, tl_outer.y, tl_inner.x, tl_inner.y),
2018                     TexelRect::new(px0, py0, px1, py1),
2019                     RepeatMode::Stretch,
2020                     RepeatMode::Stretch
2021                 );
2022                 // Top right
2023                 add_segment(
2024                     &mut segments,
2025                     LayerRect::from_floats(tr_inner.x, tr_outer.y, tr_outer.x, tr_inner.y),
2026                     TexelRect::new(px2, py0, px3, py1),
2027                     RepeatMode::Stretch,
2028                     RepeatMode::Stretch
2029                 );
2030                 // Bottom right
2031                 add_segment(
2032                     &mut segments,
2033                     LayerRect::from_floats(br_inner.x, br_inner.y, br_outer.x, br_outer.y),
2034                     TexelRect::new(px2, py2, px3, py3),
2035                     RepeatMode::Stretch,
2036                     RepeatMode::Stretch
2037                 );
2038                 // Bottom left
2039                 add_segment(
2040                     &mut segments,
2041                     LayerRect::from_floats(bl_outer.x, bl_inner.y, bl_inner.x, bl_outer.y),
2042                     TexelRect::new(px0, py2, px1, py3),
2043                     RepeatMode::Stretch,
2044                     RepeatMode::Stretch
2045                 );
2046 
2047                 // Center
2048                 if border.fill {
2049                     add_segment(
2050                         &mut segments,
2051                         LayerRect::from_floats(tl_inner.x, tl_inner.y, tr_inner.x, bl_inner.y),
2052                         TexelRect::new(px1, py1, px2, py2),
2053                         border.repeat_horizontal,
2054                         border.repeat_vertical
2055                     );
2056                 }
2057 
2058                 // Add edge segments.
2059 
2060                 // Top
2061                 add_segment(
2062                     &mut segments,
2063                     LayerRect::from_floats(tl_inner.x, tl_outer.y, tr_inner.x, tl_inner.y),
2064                     TexelRect::new(px1, py0, px2, py1),
2065                     border.repeat_horizontal,
2066                     RepeatMode::Stretch,
2067                 );
2068                 // Bottom
2069                 add_segment(
2070                     &mut segments,
2071                     LayerRect::from_floats(bl_inner.x, bl_inner.y, br_inner.x, bl_outer.y),
2072                     TexelRect::new(px1, py2, px2, py3),
2073                     border.repeat_horizontal,
2074                     RepeatMode::Stretch,
2075                 );
2076                 // Left
2077                 add_segment(
2078                     &mut segments,
2079                     LayerRect::from_floats(tl_outer.x, tl_inner.y, tl_inner.x, bl_inner.y),
2080                     TexelRect::new(px0, py1, px1, py2),
2081                     RepeatMode::Stretch,
2082                     border.repeat_vertical,
2083                 );
2084                 // Right
2085                 add_segment(
2086                     &mut segments,
2087                     LayerRect::from_floats(tr_inner.x, tr_inner.y, br_outer.x, br_inner.y),
2088                     TexelRect::new(px2, py1, px3, py2),
2089                     RepeatMode::Stretch,
2090                     border.repeat_vertical,
2091                 );
2092 
2093                 for segment in segments {
2094                     let mut info = info.clone();
2095                     info.rect = segment.geom_rect;
2096                     self.add_image(
2097                         clip_and_scroll,
2098                         &info,
2099                         segment.stretch_size,
2100                         segment.tile_spacing,
2101                         Some(segment.sub_rect),
2102                         border.image_key,
2103                         ImageRendering::Auto,
2104                         AlphaType::PremultipliedAlpha,
2105                         None,
2106                     );
2107                 }
2108             }
2109             BorderDetails::Normal(ref border) => {
2110                 self.add_normal_border(info, border, &border_item.widths, clip_and_scroll);
2111             }
2112             BorderDetails::Gradient(ref border) => for segment in create_segments(border.outset) {
2113                 let segment_rel = segment.origin - rect.origin;
2114                 let mut info = info.clone();
2115                 info.rect = segment;
2116 
2117                 self.add_gradient(
2118                     clip_and_scroll,
2119                     &info,
2120                     border.gradient.start_point - segment_rel,
2121                     border.gradient.end_point - segment_rel,
2122                     gradient_stops,
2123                     gradient_stops_count,
2124                     border.gradient.extend_mode,
2125                     segment.size,
2126                     LayerSize::zero(),
2127                 );
2128             },
2129             BorderDetails::RadialGradient(ref border) => {
2130                 for segment in create_segments(border.outset) {
2131                     let segment_rel = segment.origin - rect.origin;
2132                     let mut info = info.clone();
2133                     info.rect = segment;
2134 
2135                     self.add_radial_gradient(
2136                         clip_and_scroll,
2137                         &info,
2138                         border.gradient.start_center - segment_rel,
2139                         border.gradient.start_radius,
2140                         border.gradient.end_center - segment_rel,
2141                         border.gradient.end_radius,
2142                         border.gradient.ratio_xy,
2143                         gradient_stops,
2144                         border.gradient.extend_mode,
2145                         segment.size,
2146                         LayerSize::zero(),
2147                     );
2148                 }
2149             }
2150         }
2151     }
2152 
add_gradient_impl( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_point: LayerPoint, end_point: LayerPoint, stops: ItemRange<GradientStop>, stops_count: usize, extend_mode: ExtendMode, gradient_index: CachedGradientIndex, )2153     fn add_gradient_impl(
2154         &mut self,
2155         clip_and_scroll: ScrollNodeAndClipChain,
2156         info: &LayerPrimitiveInfo,
2157         start_point: LayerPoint,
2158         end_point: LayerPoint,
2159         stops: ItemRange<GradientStop>,
2160         stops_count: usize,
2161         extend_mode: ExtendMode,
2162         gradient_index: CachedGradientIndex,
2163     ) {
2164         // Try to ensure that if the gradient is specified in reverse, then so long as the stops
2165         // are also supplied in reverse that the rendered result will be equivalent. To do this,
2166         // a reference orientation for the gradient line must be chosen, somewhat arbitrarily, so
2167         // just designate the reference orientation as start < end. Aligned gradient rendering
2168         // manages to produce the same result regardless of orientation, so don't worry about
2169         // reversing in that case.
2170         let reverse_stops = start_point.x > end_point.x ||
2171             (start_point.x == end_point.x && start_point.y > end_point.y);
2172 
2173         // To get reftests exactly matching with reverse start/end
2174         // points, it's necessary to reverse the gradient
2175         // line in some cases.
2176         let (sp, ep) = if reverse_stops {
2177             (end_point, start_point)
2178         } else {
2179             (start_point, end_point)
2180         };
2181 
2182         let prim = BrushPrimitive::new(
2183             BrushKind::LinearGradient {
2184                 stops_range: stops,
2185                 stops_count,
2186                 extend_mode,
2187                 reverse_stops,
2188                 start_point: sp,
2189                 end_point: ep,
2190                 gradient_index,
2191             },
2192             None,
2193         );
2194 
2195         let prim = PrimitiveContainer::Brush(prim);
2196 
2197         self.add_primitive(clip_and_scroll, info, Vec::new(), prim);
2198     }
2199 
add_gradient( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_point: LayerPoint, end_point: LayerPoint, stops: ItemRange<GradientStop>, stops_count: usize, extend_mode: ExtendMode, tile_size: LayerSize, tile_spacing: LayerSize, )2200     pub fn add_gradient(
2201         &mut self,
2202         clip_and_scroll: ScrollNodeAndClipChain,
2203         info: &LayerPrimitiveInfo,
2204         start_point: LayerPoint,
2205         end_point: LayerPoint,
2206         stops: ItemRange<GradientStop>,
2207         stops_count: usize,
2208         extend_mode: ExtendMode,
2209         tile_size: LayerSize,
2210         tile_spacing: LayerSize,
2211     ) {
2212         let gradient_index = CachedGradientIndex(self.cached_gradients.len());
2213         self.cached_gradients.push(CachedGradient::new());
2214 
2215         let prim_infos = info.decompose(
2216             tile_size,
2217             tile_spacing,
2218             64 * 64,
2219         );
2220 
2221         if prim_infos.is_empty() {
2222             self.add_gradient_impl(
2223                 clip_and_scroll,
2224                 info,
2225                 start_point,
2226                 end_point,
2227                 stops,
2228                 stops_count,
2229                 extend_mode,
2230                 gradient_index,
2231             );
2232         } else {
2233             for prim_info in prim_infos {
2234                 self.add_gradient_impl(
2235                     clip_and_scroll,
2236                     &prim_info,
2237                     start_point,
2238                     end_point,
2239                     stops,
2240                     stops_count,
2241                     extend_mode,
2242                     gradient_index,
2243                 );
2244             }
2245         }
2246     }
2247 
add_radial_gradient_impl( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_center: LayerPoint, start_radius: f32, end_center: LayerPoint, end_radius: f32, ratio_xy: f32, stops: ItemRange<GradientStop>, extend_mode: ExtendMode, gradient_index: CachedGradientIndex, )2248     fn add_radial_gradient_impl(
2249         &mut self,
2250         clip_and_scroll: ScrollNodeAndClipChain,
2251         info: &LayerPrimitiveInfo,
2252         start_center: LayerPoint,
2253         start_radius: f32,
2254         end_center: LayerPoint,
2255         end_radius: f32,
2256         ratio_xy: f32,
2257         stops: ItemRange<GradientStop>,
2258         extend_mode: ExtendMode,
2259         gradient_index: CachedGradientIndex,
2260     ) {
2261         let prim = BrushPrimitive::new(
2262             BrushKind::RadialGradient {
2263                 stops_range: stops,
2264                 extend_mode,
2265                 start_center,
2266                 end_center,
2267                 start_radius,
2268                 end_radius,
2269                 ratio_xy,
2270                 gradient_index,
2271             },
2272             None,
2273         );
2274 
2275         self.add_primitive(
2276             clip_and_scroll,
2277             info,
2278             Vec::new(),
2279             PrimitiveContainer::Brush(prim),
2280         );
2281     }
2282 
add_radial_gradient( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, start_center: LayerPoint, start_radius: f32, end_center: LayerPoint, end_radius: f32, ratio_xy: f32, stops: ItemRange<GradientStop>, extend_mode: ExtendMode, tile_size: LayerSize, tile_spacing: LayerSize, )2283     pub fn add_radial_gradient(
2284         &mut self,
2285         clip_and_scroll: ScrollNodeAndClipChain,
2286         info: &LayerPrimitiveInfo,
2287         start_center: LayerPoint,
2288         start_radius: f32,
2289         end_center: LayerPoint,
2290         end_radius: f32,
2291         ratio_xy: f32,
2292         stops: ItemRange<GradientStop>,
2293         extend_mode: ExtendMode,
2294         tile_size: LayerSize,
2295         tile_spacing: LayerSize,
2296     ) {
2297         let gradient_index = CachedGradientIndex(self.cached_gradients.len());
2298         self.cached_gradients.push(CachedGradient::new());
2299 
2300         let prim_infos = info.decompose(
2301             tile_size,
2302             tile_spacing,
2303             64 * 64,
2304         );
2305 
2306         if prim_infos.is_empty() {
2307             self.add_radial_gradient_impl(
2308                 clip_and_scroll,
2309                 info,
2310                 start_center,
2311                 start_radius,
2312                 end_center,
2313                 end_radius,
2314                 ratio_xy,
2315                 stops,
2316                 extend_mode,
2317                 gradient_index,
2318             );
2319         } else {
2320             for prim_info in prim_infos {
2321                 self.add_radial_gradient_impl(
2322                     clip_and_scroll,
2323                     &prim_info,
2324                     start_center,
2325                     start_radius,
2326                     end_center,
2327                     end_radius,
2328                     ratio_xy,
2329                     stops,
2330                     extend_mode,
2331                     gradient_index,
2332                 );
2333             }
2334         }
2335     }
2336 
add_text( &mut self, clip_and_scroll: ScrollNodeAndClipChain, run_offset: LayoutVector2D, info: &LayerPrimitiveInfo, font_instance_key: &FontInstanceKey, text_color: &ColorF, glyph_range: ItemRange<GlyphInstance>, glyph_count: usize, glyph_options: Option<GlyphOptions>, )2337     pub fn add_text(
2338         &mut self,
2339         clip_and_scroll: ScrollNodeAndClipChain,
2340         run_offset: LayoutVector2D,
2341         info: &LayerPrimitiveInfo,
2342         font_instance_key: &FontInstanceKey,
2343         text_color: &ColorF,
2344         glyph_range: ItemRange<GlyphInstance>,
2345         glyph_count: usize,
2346         glyph_options: Option<GlyphOptions>,
2347     ) {
2348         let prim = {
2349             let instance_map = self.font_instances.read().unwrap();
2350             let font_instance = match instance_map.get(&font_instance_key) {
2351                 Some(instance) => instance,
2352                 None => {
2353                     warn!("Unknown font instance key");
2354                     debug!("key={:?}", font_instance_key);
2355                     return;
2356                 }
2357             };
2358 
2359             // Trivial early out checks
2360             if font_instance.size.0 <= 0 {
2361                 return;
2362             }
2363 
2364             // Sanity check - anything with glyphs bigger than this
2365             // is probably going to consume too much memory to render
2366             // efficiently anyway. This is specifically to work around
2367             // the font_advance.html reftest, which creates a very large
2368             // font as a crash test - the rendering is also ignored
2369             // by the azure renderer.
2370             if font_instance.size >= Au::from_px(4096) {
2371                 return;
2372             }
2373 
2374             // TODO(gw): Use a proper algorithm to select
2375             // whether this item should be rendered with
2376             // subpixel AA!
2377             let mut render_mode = self.config
2378                 .default_font_render_mode
2379                 .limit_by(font_instance.render_mode);
2380             let mut flags = font_instance.flags;
2381             if let Some(options) = glyph_options {
2382                 render_mode = render_mode.limit_by(options.render_mode);
2383                 flags |= options.flags;
2384             }
2385 
2386             // There are some conditions under which we can't use
2387             // subpixel text rendering, even if enabled.
2388             if render_mode == FontRenderMode::Subpixel {
2389                 // text on a picture that has filters
2390                 // (e.g. opacity) can't use sub-pixel.
2391                 // TODO(gw): It's possible we can relax this in
2392                 //           the future, if we modify the way
2393                 //           we handle subpixel blending.
2394                 if let Some(ref stacking_context) = self.sc_stack.last() {
2395                     if !stacking_context.allow_subpixel_aa {
2396                         render_mode = FontRenderMode::Alpha;
2397                     }
2398                 }
2399             }
2400 
2401             let prim_font = FontInstance::new(
2402                 font_instance.font_key,
2403                 font_instance.size,
2404                 *text_color,
2405                 font_instance.bg_color,
2406                 render_mode,
2407                 font_instance.subpx_dir,
2408                 flags,
2409                 font_instance.platform_options,
2410                 font_instance.variations.clone(),
2411             );
2412             TextRunPrimitiveCpu {
2413                 font: prim_font,
2414                 glyph_range,
2415                 glyph_count,
2416                 glyph_gpu_blocks: Vec::new(),
2417                 glyph_keys: Vec::new(),
2418                 offset: run_offset,
2419                 shadow: false,
2420             }
2421         };
2422 
2423         // Text shadows that have a blur radius of 0 need to be rendered as normal
2424         // text elements to get pixel perfect results for reftests. It's also a big
2425         // performance win to avoid blurs and render target allocations where
2426         // possible. For any text shadows that have zero blur, create a normal text
2427         // primitive with the shadow's color and offset. These need to be added
2428         // *before* the visual text primitive in order to get the correct paint
2429         // order. Store them in a Vec first to work around borrowck issues.
2430         // TODO(gw): Refactor to avoid having to store them in a Vec first.
2431         let mut fast_shadow_prims = Vec::new();
2432         for (idx, &(shadow_prim_index, _)) in self.shadow_prim_stack.iter().enumerate() {
2433             let shadow_metadata = &self.prim_store.cpu_metadata[shadow_prim_index.0];
2434             let picture_prim = &self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
2435             match picture_prim.kind {
2436                 PictureKind::TextShadow { offset, color, blur_radius, .. } if blur_radius == 0.0 => {
2437                     let mut text_prim = prim.clone();
2438                     text_prim.font.color = color.into();
2439                     text_prim.shadow = true;
2440                     text_prim.offset += offset;
2441                     fast_shadow_prims.push((idx, text_prim));
2442                 }
2443                 _ => {}
2444             }
2445         }
2446 
2447         for (idx, text_prim) in fast_shadow_prims {
2448             let rect = info.rect;
2449             let mut info = info.clone();
2450             info.rect = rect.translate(&text_prim.offset);
2451             info.local_clip =
2452               LocalClip::from(info.local_clip.clip_rect().translate(&text_prim.offset));
2453             let prim_index = self.create_primitive(
2454                 &info,
2455                 Vec::new(),
2456                 PrimitiveContainer::TextRun(text_prim),
2457             );
2458             self.shadow_prim_stack[idx].1.push((prim_index, clip_and_scroll));
2459         }
2460 
2461         // Create (and add to primitive store) the primitive that will be
2462         // used for both the visual element and also the shadow(s).
2463         let prim_index = self.create_primitive(
2464             info,
2465             Vec::new(),
2466             PrimitiveContainer::TextRun(prim),
2467         );
2468 
2469         // Only add a visual element if it can contribute to the scene.
2470         if text_color.a > 0.0 {
2471             if self.shadow_prim_stack.is_empty() {
2472                 self.add_primitive_to_hit_testing_list(info, clip_and_scroll);
2473                 self.add_primitive_to_draw_list(prim_index, clip_and_scroll);
2474             } else {
2475                 self.pending_shadow_contents.push((prim_index, clip_and_scroll, *info));
2476             }
2477         }
2478 
2479         // Now add this primitive index to all the currently active text shadow
2480         // primitives. Although we're adding the indices *after* the visual
2481         // primitive here, they will still draw before the visual text, since
2482         // the shadow primitive itself has been added to the draw cmd
2483         // list *before* the visual element, during push_shadow. We need
2484         // the primitive index of the visual element here before we can add
2485         // the indices as sub-primitives to the shadow primitives.
2486         for &(shadow_prim_index, _) in &self.shadow_prim_stack {
2487             let shadow_metadata = &mut self.prim_store.cpu_metadata[shadow_prim_index.0];
2488             debug_assert_eq!(shadow_metadata.prim_kind, PrimitiveKind::Picture);
2489             let picture =
2490                 &mut self.prim_store.cpu_pictures[shadow_metadata.cpu_prim_index.0];
2491 
2492             match picture.kind {
2493                 // Only run real blurs here (fast path zero blurs are handled above).
2494                 PictureKind::TextShadow { blur_radius, .. } if blur_radius > 0.0 => {
2495                     picture.add_primitive(
2496                         prim_index,
2497                         clip_and_scroll,
2498                     );
2499                 }
2500                 _ => {}
2501             }
2502         }
2503     }
2504 
add_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, stretch_size: LayerSize, mut tile_spacing: LayerSize, sub_rect: Option<TexelRect>, image_key: ImageKey, image_rendering: ImageRendering, alpha_type: AlphaType, tile_offset: Option<TileOffset>, )2505     pub fn add_image(
2506         &mut self,
2507         clip_and_scroll: ScrollNodeAndClipChain,
2508         info: &LayerPrimitiveInfo,
2509         stretch_size: LayerSize,
2510         mut tile_spacing: LayerSize,
2511         sub_rect: Option<TexelRect>,
2512         image_key: ImageKey,
2513         image_rendering: ImageRendering,
2514         alpha_type: AlphaType,
2515         tile_offset: Option<TileOffset>,
2516     ) {
2517         // If the tile spacing is the same as the rect size,
2518         // then it is effectively zero. We use this later on
2519         // in prim_store to detect if an image can be considered
2520         // opaque.
2521         if tile_spacing == info.rect.size {
2522             tile_spacing = LayerSize::zero();
2523         }
2524 
2525         let request = ImageRequest {
2526             key: image_key,
2527             rendering: image_rendering,
2528             tile: tile_offset,
2529         };
2530 
2531         // See if conditions are met to run through the new
2532         // image brush shader, which supports segments.
2533         if tile_spacing == LayerSize::zero() &&
2534            stretch_size == info.rect.size &&
2535            sub_rect.is_none() &&
2536            tile_offset.is_none() {
2537             let prim = BrushPrimitive::new(
2538                 BrushKind::Image {
2539                     request,
2540                     current_epoch: Epoch::invalid(),
2541                     alpha_type,
2542                 },
2543                 None,
2544             );
2545 
2546             self.add_primitive(
2547                 clip_and_scroll,
2548                 info,
2549                 Vec::new(),
2550                 PrimitiveContainer::Brush(prim),
2551             );
2552         } else {
2553             let prim_cpu = ImagePrimitiveCpu {
2554                 tile_spacing,
2555                 alpha_type,
2556                 stretch_size,
2557                 current_epoch: Epoch::invalid(),
2558                 source: ImageSource::Default,
2559                 key: ImageCacheKey {
2560                     request,
2561                     texel_rect: sub_rect.map(|texel_rect| {
2562                         DeviceIntRect::new(
2563                             DeviceIntPoint::new(
2564                                 texel_rect.uv0.x as i32,
2565                                 texel_rect.uv0.y as i32,
2566                             ),
2567                             DeviceIntSize::new(
2568                                 (texel_rect.uv1.x - texel_rect.uv0.x) as i32,
2569                                 (texel_rect.uv1.y - texel_rect.uv0.y) as i32,
2570                             ),
2571                         )
2572                     }),
2573                 },
2574             };
2575 
2576             self.add_primitive(
2577                 clip_and_scroll,
2578                 info,
2579                 Vec::new(),
2580                 PrimitiveContainer::Image(prim_cpu),
2581             );
2582         }
2583     }
2584 
add_yuv_image( &mut self, clip_and_scroll: ScrollNodeAndClipChain, info: &LayerPrimitiveInfo, yuv_data: YuvData, color_space: YuvColorSpace, image_rendering: ImageRendering, )2585     pub fn add_yuv_image(
2586         &mut self,
2587         clip_and_scroll: ScrollNodeAndClipChain,
2588         info: &LayerPrimitiveInfo,
2589         yuv_data: YuvData,
2590         color_space: YuvColorSpace,
2591         image_rendering: ImageRendering,
2592     ) {
2593         let format = yuv_data.get_format();
2594         let yuv_key = match yuv_data {
2595             YuvData::NV12(plane_0, plane_1) => [plane_0, plane_1, ImageKey::DUMMY],
2596             YuvData::PlanarYCbCr(plane_0, plane_1, plane_2) => [plane_0, plane_1, plane_2],
2597             YuvData::InterleavedYCbCr(plane_0) => [plane_0, ImageKey::DUMMY, ImageKey::DUMMY],
2598         };
2599 
2600         let prim = BrushPrimitive::new(
2601             BrushKind::YuvImage {
2602                 yuv_key,
2603                 format,
2604                 color_space,
2605                 image_rendering,
2606             },
2607             None,
2608         );
2609 
2610         self.add_primitive(
2611             clip_and_scroll,
2612             info,
2613             Vec::new(),
2614             PrimitiveContainer::Brush(prim),
2615         );
2616     }
2617 }
2618 
build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene2619 pub fn build_scene(config: &FrameBuilderConfig, request: SceneRequest) -> BuiltScene {
2620 
2621     let mut clip_scroll_tree = ClipScrollTree::new();
2622     let mut new_scene = Scene::new();
2623 
2624     let frame_builder = DisplayListFlattener::create_frame_builder(
2625         FrameBuilder::empty(), // WIP, we're not really recycling anything here, clean this up.
2626         &request.scene,
2627         &mut clip_scroll_tree,
2628         request.font_instances,
2629         request.tiled_image_map,
2630         &request.view,
2631         &request.output_pipelines,
2632         config,
2633         &mut new_scene
2634     );
2635 
2636     BuiltScene {
2637         scene: new_scene,
2638         frame_builder,
2639         clip_scroll_tree,
2640         removed_pipelines: request.removed_pipelines,
2641     }
2642 }
2643 
2644 trait PrimitiveInfoTiler {
decompose( &self, tile_size: LayerSize, tile_spacing: LayerSize, max_prims: usize, ) -> Vec<LayerPrimitiveInfo>2645     fn decompose(
2646         &self,
2647         tile_size: LayerSize,
2648         tile_spacing: LayerSize,
2649         max_prims: usize,
2650     ) -> Vec<LayerPrimitiveInfo>;
2651 }
2652 
2653 impl PrimitiveInfoTiler for LayerPrimitiveInfo {
decompose( &self, tile_size: LayerSize, tile_spacing: LayerSize, max_prims: usize, ) -> Vec<LayerPrimitiveInfo>2654     fn decompose(
2655         &self,
2656         tile_size: LayerSize,
2657         tile_spacing: LayerSize,
2658         max_prims: usize,
2659     ) -> Vec<LayerPrimitiveInfo> {
2660         let mut prims = Vec::new();
2661         let tile_repeat = tile_size + tile_spacing;
2662 
2663         if tile_repeat.width <= 0.0 ||
2664            tile_repeat.height <= 0.0 {
2665             return prims;
2666         }
2667 
2668         if tile_repeat.width < self.rect.size.width ||
2669            tile_repeat.height < self.rect.size.height {
2670             let local_clip = self.local_clip.clip_by(&self.rect);
2671             let rect_p0 = self.rect.origin;
2672             let rect_p1 = self.rect.bottom_right();
2673 
2674             let mut y0 = rect_p0.y;
2675             while y0 < rect_p1.y {
2676                 let mut x0 = rect_p0.x;
2677 
2678                 while x0 < rect_p1.x {
2679                     prims.push(LayerPrimitiveInfo {
2680                         rect: LayerRect::new(
2681                             LayerPoint::new(x0, y0),
2682                             tile_size,
2683                         ),
2684                         local_clip,
2685                         is_backface_visible: self.is_backface_visible,
2686                         tag: self.tag,
2687                     });
2688 
2689                     // Mostly a safety against a crazy number of primitives
2690                     // being generated. If we exceed that amount, just bail
2691                     // out and only draw the maximum amount.
2692                     if prims.len() > max_prims {
2693                         warn!("too many prims found due to repeat/tile. dropping extra prims!");
2694                         return prims;
2695                     }
2696 
2697                     x0 += tile_repeat.width;
2698                 }
2699 
2700                 y0 += tile_repeat.height;
2701             }
2702         }
2703 
2704         prims
2705     }
2706 }
2707 
2708 /// Properties of a stacking context that are maintained
2709 /// during creation of the scene. These structures are
2710 /// not persisted after the initial scene build.
2711 struct FlattenedStackingContext {
2712     /// Pipeline this stacking context belongs to.
2713     pipeline_id: PipelineId,
2714 
2715     /// Filters / mix-blend-mode effects
2716     composite_ops: CompositeOps,
2717 
2718     /// If true, visible when backface is visible.
2719     is_backface_visible: bool,
2720 
2721     /// Allow subpixel AA for text runs on this stacking context.
2722     /// This is a temporary hack while we don't support subpixel AA
2723     /// on transparent stacking contexts.
2724     allow_subpixel_aa: bool,
2725 
2726     /// CSS transform-style property.
2727     transform_style: TransformStyle,
2728 
2729     /// If Some(..), this stacking context establishes a new
2730     /// 3d rendering context, and the value is the primitive
2731     // index of the 3d context container.
2732     rendering_context_3d_prim_index: Option<PrimitiveIndex>,
2733 }
2734 
2735 #[derive(Debug)]
2736 pub struct ScrollbarInfo(pub ClipScrollNodeIndex, pub LayerRect);
2737