1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 extern crate yaml_rust;
6 
7 use app_units::Au;
8 use euclid::{TypedPoint2D, TypedRect, TypedSize2D, TypedTransform3D, TypedVector2D};
9 use image::{save_buffer, ColorType};
10 use premultiply::unpremultiply;
11 use scene::{Scene, SceneProperties};
12 use std::collections::HashMap;
13 use std::io::Write;
14 use std::path::{Path, PathBuf};
15 use std::{fmt, fs};
16 use super::CURRENT_FRAME_NUMBER;
17 use time;
18 use webrender;
19 use webrender::api::*;
20 use webrender::api::SpecificDisplayItem::*;
21 use webrender::api::channel::Payload;
22 use yaml_helper::StringEnum;
23 use yaml_rust::{Yaml, YamlEmitter};
24 
25 type Table = yaml_rust::yaml::Hash;
26 
array_elements_are_same<T: PartialEq>(v: &[T]) -> bool27 fn array_elements_are_same<T: PartialEq>(v: &[T]) -> bool {
28     if !v.is_empty() {
29         let first = &v[0];
30         for o in v.iter() {
31             if *first != *o {
32                 return false;
33             }
34         }
35     }
36     true
37 }
38 
new_table() -> Table39 fn new_table() -> Table {
40     Table::new()
41 }
42 
yaml_node(parent: &mut Table, key: &str, value: Yaml)43 fn yaml_node(parent: &mut Table, key: &str, value: Yaml) {
44     parent.insert(Yaml::String(key.to_owned()), value);
45 }
46 
str_node(parent: &mut Table, key: &str, value: &str)47 fn str_node(parent: &mut Table, key: &str, value: &str) {
48     yaml_node(parent, key, Yaml::String(value.to_owned()));
49 }
50 
path_node(parent: &mut Table, key: &str, value: &Path)51 fn path_node(parent: &mut Table, key: &str, value: &Path) {
52     let pstr = value.to_str().unwrap().to_owned().replace("\\", "/");
53     yaml_node(parent, key, Yaml::String(pstr));
54 }
55 
56 fn enum_node<E: StringEnum>(parent: &mut Table, key: &str, value: E) {
57     yaml_node(parent, key, Yaml::String(value.as_str().to_owned()));
58 }
59 
60 fn color_to_string(value: ColorF) -> String {
61     if value.r == 1.0 && value.g == 1.0 && value.b == 1.0 && value.a == 1.0 {
62         "white".to_owned()
63     } else if value.r == 0.0 && value.g == 0.0 && value.b == 0.0 && value.a == 1.0 {
64         "black".to_owned()
65     } else {
66         format!(
67             "{} {} {} {:.4}",
68             value.r * 255.0,
69             value.g * 255.0,
70             value.b * 255.0,
71             value.a
72         )
73     }
74 }
75 
76 fn color_node(parent: &mut Table, key: &str, value: ColorF) {
77     yaml_node(parent, key, Yaml::String(color_to_string(value)));
78 }
79 
80 fn point_node<U>(parent: &mut Table, key: &str, value: &TypedPoint2D<f32, U>) {
81     f32_vec_node(parent, key, &[value.x, value.y]);
82 }
83 
84 fn vector_node<U>(parent: &mut Table, key: &str, value: &TypedVector2D<f32, U>) {
85     f32_vec_node(parent, key, &[value.x, value.y]);
86 }
87 
88 fn size_node<U>(parent: &mut Table, key: &str, value: &TypedSize2D<f32, U>) {
89     f32_vec_node(parent, key, &[value.width, value.height]);
90 }
91 
92 fn rect_yaml<U>(value: &TypedRect<f32, U>) -> Yaml {
93     f32_vec_yaml(
94         &[
95             value.origin.x,
96             value.origin.y,
97             value.size.width,
98             value.size.height,
99         ],
100         false,
101     )
102 }
103 
104 fn rect_node<U>(parent: &mut Table, key: &str, value: &TypedRect<f32, U>) {
105     yaml_node(parent, key, rect_yaml(value));
106 }
107 
108 fn matrix4d_node<U1, U2>(parent: &mut Table, key: &str, value: &TypedTransform3D<f32, U1, U2>) {
109     f32_vec_node(parent, key, &value.to_row_major_array());
110 }
111 
112 fn u32_node(parent: &mut Table, key: &str, value: u32) {
113     yaml_node(parent, key, Yaml::Integer(value as i64));
114 }
115 
116 fn usize_node(parent: &mut Table, key: &str, value: usize) {
117     yaml_node(parent, key, Yaml::Integer(value as i64));
118 }
119 
120 fn f32_node(parent: &mut Table, key: &str, value: f32) {
121     yaml_node(parent, key, Yaml::Real(value.to_string()));
122 }
123 
124 fn bool_node(parent: &mut Table, key: &str, value: bool) {
125     yaml_node(parent, key, Yaml::Boolean(value));
126 }
127 
128 fn table_node(parent: &mut Table, key: &str, value: Table) {
129     yaml_node(parent, key, Yaml::Hash(value));
130 }
131 
132 fn string_vec_yaml(value: &[String], check_unique: bool) -> Yaml {
133     if !value.is_empty() && check_unique && array_elements_are_same(value) {
134         Yaml::String(value[0].clone())
135     } else {
136         Yaml::Array(value.iter().map(|v| Yaml::String(v.clone())).collect())
137     }
138 }
139 
140 fn u32_vec_yaml(value: &[u32], check_unique: bool) -> Yaml {
141     if !value.is_empty() && check_unique && array_elements_are_same(value) {
142         Yaml::Integer(value[0] as i64)
143     } else {
144         Yaml::Array(value.iter().map(|v| Yaml::Integer(*v as i64)).collect())
145     }
146 }
147 
148 fn u32_vec_node(parent: &mut Table, key: &str, value: &[u32]) {
149     yaml_node(parent, key, u32_vec_yaml(value, false));
150 }
151 
152 fn f32_vec_yaml(value: &[f32], check_unique: bool) -> Yaml {
153     if !value.is_empty() && check_unique && array_elements_are_same(value) {
154         Yaml::Real(value[0].to_string())
155     } else {
156         Yaml::Array(value.iter().map(|v| Yaml::Real(v.to_string())).collect())
157     }
158 }
159 
160 fn f32_vec_node(parent: &mut Table, key: &str, value: &[f32]) {
161     yaml_node(parent, key, f32_vec_yaml(value, false));
162 }
163 
164 fn maybe_radius_yaml(radius: &BorderRadius) -> Option<Yaml> {
165     if let Some(radius) = radius.is_uniform_size() {
166         if radius == LayoutSize::zero() {
167             None
168         } else {
169             Some(f32_vec_yaml(&[radius.width, radius.height], false))
170         }
171     } else {
172         let mut table = new_table();
173         size_node(&mut table, "top-left", &radius.top_left);
174         size_node(&mut table, "top-right", &radius.top_right);
175         size_node(&mut table, "bottom-left", &radius.bottom_left);
176         size_node(&mut table, "bottom-right", &radius.bottom_right);
177         Some(Yaml::Hash(table))
178     }
179 }
180 
181 fn write_sc(parent: &mut Table, sc: &StackingContext, properties: &SceneProperties, filter_iter: AuxIter<FilterOp>) {
182     enum_node(parent, "scroll-policy", sc.scroll_policy);
183 
184     matrix4d_node(parent, "transform", &properties.resolve_layout_transform(&sc.transform));
185 
186     enum_node(parent, "transform-style", sc.transform_style);
187 
188     if let Some(perspective) = sc.perspective {
189         matrix4d_node(parent, "perspective", &perspective);
190     }
191 
192     // mix_blend_mode
193     if sc.mix_blend_mode != MixBlendMode::Normal {
194         enum_node(parent, "mix-blend-mode", sc.mix_blend_mode)
195     }
196     // filters
197     let mut filters = vec![];
198     for filter in filter_iter {
199         match filter {
200             FilterOp::Blur(x) => { filters.push(Yaml::String(format!("blur({})", x))) }
201             FilterOp::Brightness(x) => { filters.push(Yaml::String(format!("brightness({})", x))) }
202             FilterOp::Contrast(x) => { filters.push(Yaml::String(format!("contrast({})", x))) }
203             FilterOp::Grayscale(x) => { filters.push(Yaml::String(format!("grayscale({})", x))) }
204             FilterOp::HueRotate(x) => { filters.push(Yaml::String(format!("hue-rotate({})", x))) }
205             FilterOp::Invert(x) => { filters.push(Yaml::String(format!("invert({})", x))) }
206             FilterOp::Opacity(x, _) => {
207                 filters.push(Yaml::String(format!("opacity({})",
208                                                   properties.resolve_float(&x, 1.0))))
209             }
210             FilterOp::Saturate(x) => { filters.push(Yaml::String(format!("saturate({})", x))) }
211             FilterOp::Sepia(x) => { filters.push(Yaml::String(format!("sepia({})", x))) }
212             FilterOp::DropShadow(offset, blur, color) => {
213                 filters.push(Yaml::String(format!("drop-shadow([{},{}],{},[{}])",
214                                                   offset.x, offset.y,
215                                                   blur,
216                                                   color_to_string(color))))
217             }
218             FilterOp::ColorMatrix(matrix) => {
219                 filters.push(Yaml::String(format!("color-matrix({:?})", matrix)))
220             }
221         }
222     }
223 
224     yaml_node(parent, "filters", Yaml::Array(filters));
225 }
226 
227 #[cfg(target_os = "windows")]
228 fn native_font_handle_to_yaml(
229     _rsrc: &mut ResourceGenerator,
230     handle: &NativeFontHandle,
231     parent: &mut yaml_rust::yaml::Hash,
232     _: &mut Option<PathBuf>,
233 ) {
234     str_node(parent, "family", &handle.family_name);
235     u32_node(parent, "weight", handle.weight.to_u32());
236     u32_node(parent, "style", handle.style.to_u32());
237     u32_node(parent, "stretch", handle.stretch.to_u32());
238 }
239 
240 #[cfg(target_os = "macos")]
241 fn native_font_handle_to_yaml(
242     rsrc: &mut ResourceGenerator,
243     handle: &NativeFontHandle,
244     parent: &mut yaml_rust::yaml::Hash,
245     path_opt: &mut Option<PathBuf>,
246 ) {
247     let path = match *path_opt {
248         Some(ref path) => { path.clone() },
249         None => {
250             use cgfont_to_data;
251             let bytes = cgfont_to_data::font_to_data(handle.0.clone()).unwrap();
252             let (path_file, path) = rsrc.next_rsrc_paths(
253                 "font",
254                 "ttf",
255             );
256             let mut file = fs::File::create(&path_file).unwrap();
257             file.write_all(&bytes).unwrap();
258             *path_opt = Some(path.clone());
259             path
260         }
261     };
262 
263     path_node(parent, "font", &path);
264 }
265 
266 #[cfg(not(any(target_os = "macos", target_os = "windows")))]
267 fn native_font_handle_to_yaml(
268     _rsrc: &mut ResourceGenerator,
269     handle: &NativeFontHandle,
270     parent: &mut yaml_rust::yaml::Hash,
271     _: &mut Option<PathBuf>,
272 ) {
273     str_node(parent, "font", &handle.pathname);
274 }
275 
276 enum CachedFont {
277     Native(NativeFontHandle, Option<PathBuf>),
278     Raw(Option<Vec<u8>>, u32, Option<PathBuf>),
279 }
280 
281 struct CachedFontInstance {
282     font_key: FontKey,
283     glyph_size: Au,
284 }
285 
286 struct CachedImage {
287     width: u32,
288     height: u32,
289     stride: u32,
290     format: ImageFormat,
291     bytes: Option<Vec<u8>>,
292     path: Option<PathBuf>,
293     tiling: Option<u16>,
294 }
295 
296 struct ResourceGenerator {
297     base: PathBuf,
298     next_num: u32,
299     prefix: String,
300 }
301 
302 impl ResourceGenerator {
303     fn next_rsrc_paths(&mut self, base: &str, ext: &str) -> (PathBuf, PathBuf) {
304         let mut path_file = self.base.to_owned();
305         let mut path = PathBuf::from("res");
306 
307         let fstr = format!("{}-{}-{}.{}", self.prefix, base, self.next_num, ext);
308         path_file.push(&fstr);
309         path.push(&fstr);
310 
311         self.next_num += 1;
312 
313         (path_file, path)
314     }
315 }
316 
317 pub struct YamlFrameWriter {
318     frame_base: PathBuf,
319     rsrc_gen: ResourceGenerator,
320     images: HashMap<ImageKey, CachedImage>,
321     fonts: HashMap<FontKey, CachedFont>,
322     font_instances: HashMap<FontInstanceKey, CachedFontInstance>,
323 
324     last_frame_written: u32,
325     pipeline_id: Option<PipelineId>,
326 
327     dl_descriptor: Option<BuiltDisplayListDescriptor>,
328 }
329 
330 pub struct YamlFrameWriterReceiver {
331     frame_writer: YamlFrameWriter,
332     scene: Scene,
333 }
334 
335 impl YamlFrameWriterReceiver {
336     pub fn new(path: &Path) -> YamlFrameWriterReceiver {
337         YamlFrameWriterReceiver {
338             frame_writer: YamlFrameWriter::new(path),
339             scene: Scene::new(),
340         }
341     }
342 }
343 
344 impl fmt::Debug for YamlFrameWriterReceiver {
345     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
346         write!(f, "YamlFrameWriterReceiver")
347     }
348 }
349 
350 impl YamlFrameWriter {
351     pub fn new(path: &Path) -> YamlFrameWriter {
352         let mut rsrc_base = path.to_owned();
353         rsrc_base.push("res");
354         fs::create_dir_all(&rsrc_base).ok();
355 
356         let rsrc_prefix = format!("{}", time::get_time().sec);
357 
358         YamlFrameWriter {
359             frame_base: path.to_owned(),
360             rsrc_gen: ResourceGenerator {
361                 base: rsrc_base,
362                 prefix: rsrc_prefix,
363                 next_num: 1,
364             },
365             images: HashMap::new(),
366             fonts: HashMap::new(),
367             font_instances: HashMap::new(),
368 
369             dl_descriptor: None,
370 
371             pipeline_id: None,
372 
373             last_frame_written: u32::max_value(),
374         }
375     }
376 
377     pub fn begin_write_display_list(
378         &mut self,
379         scene: &mut Scene,
380         epoch: &Epoch,
381         pipeline_id: &PipelineId,
382         background_color: &Option<ColorF>,
383         viewport_size: &LayoutSize,
384         display_list: &BuiltDisplayListDescriptor,
385     ) {
386         unsafe {
387             if CURRENT_FRAME_NUMBER == self.last_frame_written {
388                 return;
389             }
390             self.last_frame_written = CURRENT_FRAME_NUMBER;
391         }
392 
393         self.dl_descriptor = Some(display_list.clone());
394         self.pipeline_id = Some(pipeline_id.clone());
395 
396         scene.begin_display_list(pipeline_id, epoch, background_color, viewport_size);
397     }
398 
399     pub fn finish_write_display_list(&mut self, scene: &mut Scene, data: &[u8]) {
400         let dl_desc = self.dl_descriptor.take().unwrap();
401 
402         let payload = Payload::from_data(data);
403 
404         let dl = BuiltDisplayList::from_data(payload.display_list_data, dl_desc);
405 
406         let mut root_dl_table = new_table();
407         {
408             let mut iter = dl.iter();
409             self.write_display_list(&mut root_dl_table, &dl, scene, &mut iter, &mut ClipIdMapper::new());
410         }
411 
412         let mut root = new_table();
413         if let Some(root_pipeline_id) = scene.root_pipeline_id {
414             u32_vec_node(
415                 &mut root_dl_table,
416                 "id",
417                 &[root_pipeline_id.0, root_pipeline_id.1],
418             );
419 
420             let mut referenced_pipeline_ids = vec![];
421             let mut traversal = dl.iter();
422             while let Some(item) = traversal.next() {
423                 if let &SpecificDisplayItem::Iframe(k) = item.item() {
424                     referenced_pipeline_ids.push(k.pipeline_id);
425                 }
426             }
427 
428             let mut pipelines = vec![];
429             for pipeline_id in referenced_pipeline_ids {
430                 if !scene.display_lists.contains_key(&pipeline_id) {
431                     continue;
432                 }
433                 let mut pipeline = new_table();
434                 u32_vec_node(&mut pipeline, "id", &[pipeline_id.0, pipeline_id.1]);
435 
436                 let dl = scene.display_lists.get(&pipeline_id).unwrap();
437                 let mut iter = dl.iter();
438                 self.write_display_list(&mut pipeline, &dl, scene, &mut iter, &mut ClipIdMapper::new());
439                 pipelines.push(Yaml::Hash(pipeline));
440             }
441 
442             table_node(&mut root, "root", root_dl_table);
443 
444             root.insert(Yaml::String("pipelines".to_owned()), Yaml::Array(pipelines));
445 
446             let mut s = String::new();
447             // FIXME YamlEmitter wants a std::fmt::Write, not a io::Write, so we can't pass a file
448             // directly.  This seems broken.
449             {
450                 let mut emitter = YamlEmitter::new(&mut s);
451                 emitter.dump(&Yaml::Hash(root)).unwrap();
452             }
453             let sb = s.into_bytes();
454             let mut frame_file_name = self.frame_base.clone();
455             let current_shown_frame = unsafe { CURRENT_FRAME_NUMBER };
456             frame_file_name.push(format!("frame-{}.yaml", current_shown_frame));
457             let mut file = fs::File::create(&frame_file_name).unwrap();
458             file.write_all(&sb).unwrap();
459         }
460 
461         scene.finish_display_list(self.pipeline_id.unwrap(), dl);
462     }
463 
464     fn update_resources(&mut self, updates: &ResourceUpdates) {
465         for update in &updates.updates {
466             match *update {
467                 ResourceUpdate::AddImage(ref img) => {
468                     if let Some(ref data) = self.images.get(&img.key) {
469                           if data.path.is_some() {
470                               return;
471                           }
472                     }
473 
474                     let stride = img.descriptor.stride.unwrap_or(
475                         img.descriptor.width * img.descriptor.format.bytes_per_pixel(),
476                     );
477                     let bytes = match img.data {
478                         ImageData::Raw(ref v) => (**v).clone(),
479                         ImageData::External(_) | ImageData::Blob(_) => {
480                             return;
481                         }
482                     };
483                     self.images.insert(
484                         img.key,
485                         CachedImage {
486                             width: img.descriptor.width,
487                             height: img.descriptor.height,
488                             stride,
489                             format: img.descriptor.format,
490                             bytes: Some(bytes),
491                             tiling: img.tiling,
492                             path: None,
493                         },
494                     );
495                 }
496                 ResourceUpdate::UpdateImage(ref img) => {
497                     if let Some(ref mut data) = self.images.get_mut(&img.key) {
498                         assert_eq!(data.width, img.descriptor.width);
499                         assert_eq!(data.height, img.descriptor.height);
500                         assert_eq!(data.format, img.descriptor.format);
501 
502                         if let ImageData::Raw(ref bytes) = img.data {
503                             data.path = None;
504                             data.bytes = Some((**bytes).clone());
505                         } else {
506                             // Other existing image types only make sense within the gecko integration.
507                             println!(
508                                 "Wrench only supports updating buffer images ({}).",
509                                 "ignoring update command"
510                             );
511                         }
512                     }
513                 }
514                 ResourceUpdate::DeleteImage(img) => {
515                     self.images.remove(&img);
516                 }
517                 ResourceUpdate::AddFont(ref font) => match font {
518                     &AddFont::Raw(key, ref bytes, index) => {
519                         self.fonts
520                             .insert(key, CachedFont::Raw(Some(bytes.clone()), index, None));
521                     }
522                     &AddFont::Native(key, ref handle) => {
523                         self.fonts.insert(key, CachedFont::Native(handle.clone(), None));
524                     }
525                 },
526                 ResourceUpdate::DeleteFont(_) => {}
527                 ResourceUpdate::AddFontInstance(ref instance) => {
528                     self.font_instances.insert(
529                         instance.key,
530                         CachedFontInstance {
531                             font_key: instance.font_key,
532                             glyph_size: instance.glyph_size,
533                         },
534                     );
535                 }
536                 ResourceUpdate::DeleteFontInstance(_) => {}
537             }
538         }
539     }
540 
541 
542 
543     fn path_for_image(&mut self, key: ImageKey) -> Option<PathBuf> {
544         let data = match self.images.get_mut(&key) {
545             Some(data) => data,
546             None => return None,
547         };
548 
549         if data.path.is_some() {
550             return data.path.clone();
551         }
552         let mut bytes = data.bytes.take().unwrap();
553         let (path_file, path) = self.rsrc_gen.next_rsrc_paths(
554             "img",
555             "png",
556         );
557 
558         assert!(data.stride > 0);
559         let (color_type, bpp) = match data.format {
560             ImageFormat::BGRA8 => (ColorType::RGBA(8), 4),
561             ImageFormat::R8 => (ColorType::Gray(8), 1),
562             _ => {
563                 println!(
564                     "Failed to write image with format {:?}, dimensions {}x{}, stride {}",
565                     data.format,
566                     data.width,
567                     data.height,
568                     data.stride
569                 );
570                 return None;
571             }
572         };
573 
574         if data.stride == data.width * bpp {
575             if data.format == ImageFormat::BGRA8 {
576                 unpremultiply(bytes.as_mut_slice());
577             }
578             save_buffer(&path_file, &bytes, data.width, data.height, color_type).unwrap();
579         } else {
580             // takes a buffer with a stride and copies it into a new buffer that has stride == width
581             assert!(data.stride > data.width * bpp);
582             let mut tmp: Vec<_> = bytes[..]
583                 .chunks(data.stride as usize)
584                 .flat_map(|chunk| {
585                     chunk[.. (data.width * bpp) as usize].iter().cloned()
586                 })
587                 .collect();
588             if data.format == ImageFormat::BGRA8 {
589                 unpremultiply(tmp.as_mut_slice());
590             }
591 
592             save_buffer(&path_file, &tmp, data.width, data.height, color_type).unwrap();
593         }
594 
595         data.path = Some(path.clone());
596         Some(path)
597     }
598 
599     fn make_complex_clip_node(&mut self, complex_clip: &ComplexClipRegion) -> Yaml {
600         let mut t = new_table();
601         rect_node(&mut t, "rect", &complex_clip.rect);
602         yaml_node(
603             &mut t,
604             "radius",
605             maybe_radius_yaml(&complex_clip.radii).unwrap(),
606         );
607         enum_node(&mut t, "clip-mode", complex_clip.mode);
608         Yaml::Hash(t)
609     }
610 
611     fn make_complex_clips_node(
612         &mut self,
613         complex_clip_count: usize,
614         complex_clips: ItemRange<ComplexClipRegion>,
615         list: &BuiltDisplayList,
616     ) -> Option<Yaml> {
617         if complex_clip_count == 0 {
618             return None;
619         }
620 
621         let complex_items = list.get(complex_clips)
622             .map(|ccx| if ccx.radii.is_zero() {
623                 rect_yaml(&ccx.rect)
624             } else {
625                 self.make_complex_clip_node(&ccx)
626             })
627             .collect();
628         Some(Yaml::Array(complex_items))
629     }
630 
631     fn make_clip_mask_image_node(&mut self, image_mask: &Option<ImageMask>) -> Option<Yaml> {
632         let mask = match image_mask {
633             &Some(ref mask) => mask,
634             &None => return None,
635         };
636 
637         let mut mask_table = new_table();
638         if let Some(path) = self.path_for_image(mask.image) {
639             path_node(&mut mask_table, "image", &path);
640         }
641         rect_node(&mut mask_table, "rect", &mask.rect);
642         bool_node(&mut mask_table, "repeat", mask.repeat);
643         Some(Yaml::Hash(mask_table))
644     }
645 
646     fn write_display_list_items(
647         &mut self,
648         list: &mut Vec<Yaml>,
649         display_list: &BuiltDisplayList,
650         scene: &Scene,
651         list_iterator: &mut BuiltDisplayListIter,
652         clip_id_mapper: &mut ClipIdMapper,
653     ) {
654         // continue_traversal is a big borrowck hack
655         let mut continue_traversal = None;
656         loop {
657             if let Some(traversal) = continue_traversal.take() {
658                 *list_iterator = traversal;
659             }
660             let base = match list_iterator.next() {
661                 Some(base) => base,
662                 None => break,
663             };
664 
665             let mut v = new_table();
666             rect_node(&mut v, "bounds", &base.rect());
667 
668             rect_node(&mut v, "clip-rect", base.local_clip().clip_rect());
669             if let &LocalClip::RoundedRect(_, ref region) = base.local_clip() {
670                 yaml_node(&mut v, "complex-clip", self.make_complex_clip_node(region));
671             }
672 
673             let clip_and_scroll_yaml = match clip_id_mapper.map_info(&base.clip_and_scroll()) {
674                 (scroll_id, Some(clip_id)) => {
675                     Yaml::Array(vec![Yaml::Integer(scroll_id), Yaml::Integer(clip_id)])
676                 }
677                 (scroll_id, None) => Yaml::Integer(scroll_id),
678             };
679             yaml_node(&mut v, "clip-and-scroll", clip_and_scroll_yaml);
680             bool_node(&mut v, "backface-visible", base.is_backface_visible());
681 
682             match *base.item() {
683                 Rectangle(item) => {
684                     str_node(&mut v, "type", "rect");
685                     color_node(&mut v, "color", item.color);
686                 }
687                 ClearRectangle => {
688                     str_node(&mut v, "type", "clear-rect");;
689                 }
690                 Line(item) => {
691                     str_node(&mut v, "type", "line");
692                     if let LineStyle::Wavy = item.style {
693                         f32_node(&mut v, "thickness", item.wavy_line_thickness);
694                     }
695                     str_node(&mut v, "orientation", item.orientation.as_str());
696                     color_node(&mut v, "color", item.color);
697                     str_node(&mut v, "style", item.style.as_str());
698                 }
699                 Text(item) => {
700                     let gi = display_list.get(base.glyphs());
701                     let mut indices: Vec<u32> = vec![];
702                     let mut offsets: Vec<f32> = vec![];
703                     for g in gi {
704                         indices.push(g.index);
705                         offsets.push(g.point.x);
706                         offsets.push(g.point.y);
707                     }
708                     u32_vec_node(&mut v, "glyphs", &indices);
709                     f32_vec_node(&mut v, "offsets", &offsets);
710 
711                     let instance = self.font_instances.entry(item.font_key).or_insert_with(|| {
712                         println!("Warning: font instance key not found in font instances table!");
713                         CachedFontInstance {
714                             font_key: FontKey::new(IdNamespace(0), 0),
715                             glyph_size: Au::from_px(16),
716                         }
717                     });
718 
719                     f32_node(
720                         &mut v,
721                         "size",
722                         instance.glyph_size.to_f32_px() * 12.0 / 16.0,
723                     );
724                     color_node(&mut v, "color", item.color);
725 
726                     let entry = self.fonts.entry(instance.font_key).or_insert_with(|| {
727                         println!("Warning: font key not found in fonts table!");
728                         CachedFont::Raw(Some(vec![]), 0, None)
729                     });
730 
731                     match entry {
732                         &mut CachedFont::Native(ref handle, ref mut path_opt) => {
733                             native_font_handle_to_yaml(&mut self.rsrc_gen, handle, &mut v, path_opt);
734                         }
735                         &mut CachedFont::Raw(ref mut bytes_opt, index, ref mut path_opt) => {
736                             if let Some(bytes) = bytes_opt.take() {
737                                 let (path_file, path) = self.rsrc_gen.next_rsrc_paths(
738                                     "font",
739                                     "ttf",
740                                 );
741                                 let mut file = fs::File::create(&path_file).unwrap();
742                                 file.write_all(&bytes).unwrap();
743                                 *path_opt = Some(path);
744                             }
745 
746                             path_node(&mut v, "font", path_opt.as_ref().unwrap());
747                             if index != 0 {
748                                 u32_node(&mut v, "font-index", index);
749                             }
750                         }
751                     }
752                 }
753                 Image(item) => {
754                     if let Some(path) = self.path_for_image(item.image_key) {
755                         path_node(&mut v, "image", &path);
756                     }
757                     if let Some(&CachedImage {
758                         tiling: Some(tile_size),
759                         ..
760                     }) = self.images.get(&item.image_key)
761                     {
762                         u32_node(&mut v, "tile-size", tile_size as u32);
763                     }
764                     size_node(&mut v, "stretch-size", &item.stretch_size);
765                     size_node(&mut v, "tile-spacing", &item.tile_spacing);
766                     match item.image_rendering {
767                         ImageRendering::Auto => (),
768                         ImageRendering::CrispEdges => str_node(&mut v, "rendering", "crisp-edges"),
769                         ImageRendering::Pixelated => str_node(&mut v, "rendering", "pixelated"),
770                     };
771                     match item.alpha_type {
772                         AlphaType::PremultipliedAlpha => str_node(&mut v, "alpha-type", "premultiplied-alpha"),
773                         AlphaType::Alpha => str_node(&mut v, "alpha-type", "alpha"),
774                     };
775                 }
776                 YuvImage(_) => {
777                     str_node(&mut v, "type", "yuv-image");
778                     // TODO
779                     println!("TODO YAML YuvImage");
780                 }
781                 Border(item) => {
782                     str_node(&mut v, "type", "border");
783                     match item.details {
784                         BorderDetails::Normal(ref details) => {
785                             let trbl =
786                                 vec![&details.top, &details.right, &details.bottom, &details.left];
787                             let widths: Vec<f32> = vec![
788                                 item.widths.top,
789                                 item.widths.right,
790                                 item.widths.bottom,
791                                 item.widths.left,
792                             ];
793                             let colors: Vec<String> =
794                                 trbl.iter().map(|x| color_to_string(x.color)).collect();
795                             let styles: Vec<String> = trbl.iter()
796                                 .map(|x| {
797                                     match x.style {
798                                         BorderStyle::None => "none",
799                                         BorderStyle::Solid => "solid",
800                                         BorderStyle::Double => "double",
801                                         BorderStyle::Dotted => "dotted",
802                                         BorderStyle::Dashed => "dashed",
803                                         BorderStyle::Hidden => "hidden",
804                                         BorderStyle::Ridge => "ridge",
805                                         BorderStyle::Inset => "inset",
806                                         BorderStyle::Outset => "outset",
807                                         BorderStyle::Groove => "groove",
808                                     }.to_owned()
809                                 })
810                                 .collect();
811                             yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
812                             str_node(&mut v, "border-type", "normal");
813                             yaml_node(&mut v, "color", string_vec_yaml(&colors, true));
814                             yaml_node(&mut v, "style", string_vec_yaml(&styles, true));
815                             if let Some(radius_node) = maybe_radius_yaml(&details.radius) {
816                                 yaml_node(&mut v, "radius", radius_node);
817                             }
818                         }
819                         BorderDetails::Image(ref details) => {
820                             let widths: Vec<f32> = vec![
821                                 item.widths.top,
822                                 item.widths.right,
823                                 item.widths.bottom,
824                                 item.widths.left,
825                             ];
826                             let outset: Vec<f32> = vec![
827                                 details.outset.top,
828                                 details.outset.right,
829                                 details.outset.bottom,
830                                 details.outset.left,
831                             ];
832                             yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
833                             str_node(&mut v, "border-type", "image");
834                             if let Some(path) = self.path_for_image(details.image_key) {
835                                 path_node(&mut v, "image", &path);
836                             }
837                             u32_node(&mut v, "image-width", details.patch.width);
838                             u32_node(&mut v, "image-height", details.patch.height);
839                             let slice: Vec<u32> = vec![
840                                 details.patch.slice.top,
841                                 details.patch.slice.right,
842                                 details.patch.slice.bottom,
843                                 details.patch.slice.left,
844                             ];
845                             yaml_node(&mut v, "slice", u32_vec_yaml(&slice, true));
846                             yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
847                             match details.repeat_horizontal {
848                                 RepeatMode::Stretch => {
849                                     str_node(&mut v, "repeat-horizontal", "stretch")
850                                 }
851                                 RepeatMode::Repeat => {
852                                     str_node(&mut v, "repeat-horizontal", "repeat")
853                                 }
854                                 RepeatMode::Round => str_node(&mut v, "repeat-horizontal", "round"),
855                                 RepeatMode::Space => str_node(&mut v, "repeat-horizontal", "space"),
856                             };
857                             match details.repeat_vertical {
858                                 RepeatMode::Stretch => {
859                                     str_node(&mut v, "repeat-vertical", "stretch")
860                                 }
861                                 RepeatMode::Repeat => str_node(&mut v, "repeat-vertical", "repeat"),
862                                 RepeatMode::Round => str_node(&mut v, "repeat-vertical", "round"),
863                                 RepeatMode::Space => str_node(&mut v, "repeat-vertical", "space"),
864                             };
865                         }
866                         BorderDetails::Gradient(ref details) => {
867                             let widths: Vec<f32> = vec![
868                                 item.widths.top,
869                                 item.widths.right,
870                                 item.widths.bottom,
871                                 item.widths.left,
872                             ];
873                             let outset: Vec<f32> = vec![
874                                 details.outset.top,
875                                 details.outset.right,
876                                 details.outset.bottom,
877                                 details.outset.left,
878                             ];
879                             yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
880                             str_node(&mut v, "border-type", "gradient");
881                             point_node(&mut v, "start", &details.gradient.start_point);
882                             point_node(&mut v, "end", &details.gradient.end_point);
883                             let mut stops = vec![];
884                             for stop in display_list.get(base.gradient_stops()) {
885                                 stops.push(Yaml::Real(stop.offset.to_string()));
886                                 stops.push(Yaml::String(color_to_string(stop.color)));
887                             }
888                             yaml_node(&mut v, "stops", Yaml::Array(stops));
889                             bool_node(
890                                 &mut v,
891                                 "repeat",
892                                 details.gradient.extend_mode == ExtendMode::Repeat,
893                             );
894                             yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
895                         }
896                         BorderDetails::RadialGradient(ref details) => {
897                             let widths: Vec<f32> = vec![
898                                 item.widths.top,
899                                 item.widths.right,
900                                 item.widths.bottom,
901                                 item.widths.left,
902                             ];
903                             let outset: Vec<f32> = vec![
904                                 details.outset.top,
905                                 details.outset.right,
906                                 details.outset.bottom,
907                                 details.outset.left,
908                             ];
909                             yaml_node(&mut v, "width", f32_vec_yaml(&widths, true));
910                             str_node(&mut v, "border-type", "radial-gradient");
911                             point_node(&mut v, "start-center", &details.gradient.start_center);
912                             f32_node(&mut v, "start-radius", details.gradient.start_radius);
913                             point_node(&mut v, "end-center", &details.gradient.end_center);
914                             f32_node(&mut v, "end-radius", details.gradient.end_radius);
915                             f32_node(&mut v, "ratio-xy", details.gradient.ratio_xy);
916                             let mut stops = vec![];
917                             for stop in display_list.get(base.gradient_stops()) {
918                                 stops.push(Yaml::Real(stop.offset.to_string()));
919                                 stops.push(Yaml::String(color_to_string(stop.color)));
920                             }
921                             yaml_node(&mut v, "stops", Yaml::Array(stops));
922                             bool_node(
923                                 &mut v,
924                                 "repeat",
925                                 details.gradient.extend_mode == ExtendMode::Repeat,
926                             );
927                             yaml_node(&mut v, "outset", f32_vec_yaml(&outset, true));
928                         }
929                     }
930                 }
931                 BoxShadow(item) => {
932                     str_node(&mut v, "type", "box-shadow");
933                     rect_node(&mut v, "box-bounds", &item.box_bounds);
934                     vector_node(&mut v, "offset", &item.offset);
935                     color_node(&mut v, "color", item.color);
936                     f32_node(&mut v, "blur-radius", item.blur_radius);
937                     f32_node(&mut v, "spread-radius", item.spread_radius);
938                     if let Some(radius_node) = maybe_radius_yaml(&item.border_radius) {
939                         yaml_node(&mut v, "border-radius", radius_node);
940                     }
941                     let clip_mode = match item.clip_mode {
942                         BoxShadowClipMode::Outset => "outset",
943                         BoxShadowClipMode::Inset => "inset",
944                     };
945                     str_node(&mut v, "clip-mode", clip_mode);
946                 }
947                 Gradient(item) => {
948                     str_node(&mut v, "type", "gradient");
949                     point_node(&mut v, "start", &item.gradient.start_point);
950                     point_node(&mut v, "end", &item.gradient.end_point);
951                     size_node(&mut v, "tile-size", &item.tile_size);
952                     size_node(&mut v, "tile-spacing", &item.tile_spacing);
953                     let mut stops = vec![];
954                     for stop in display_list.get(base.gradient_stops()) {
955                         stops.push(Yaml::Real(stop.offset.to_string()));
956                         stops.push(Yaml::String(color_to_string(stop.color)));
957                     }
958                     yaml_node(&mut v, "stops", Yaml::Array(stops));
959                     bool_node(
960                         &mut v,
961                         "repeat",
962                         item.gradient.extend_mode == ExtendMode::Repeat,
963                     );
964                 }
965                 RadialGradient(item) => {
966                     str_node(&mut v, "type", "radial-gradient");
967                     point_node(&mut v, "start-center", &item.gradient.start_center);
968                     f32_node(&mut v, "start-radius", item.gradient.start_radius);
969                     point_node(&mut v, "end-center", &item.gradient.end_center);
970                     f32_node(&mut v, "end-radius", item.gradient.end_radius);
971                     f32_node(&mut v, "ratio-xy", item.gradient.ratio_xy);
972                     size_node(&mut v, "tile-size", &item.tile_size);
973                     size_node(&mut v, "tile-spacing", &item.tile_spacing);
974                     let mut stops = vec![];
975                     for stop in display_list.get(base.gradient_stops()) {
976                         stops.push(Yaml::Real(stop.offset.to_string()));
977                         stops.push(Yaml::String(color_to_string(stop.color)));
978                     }
979                     yaml_node(&mut v, "stops", Yaml::Array(stops));
980                     bool_node(
981                         &mut v,
982                         "repeat",
983                         item.gradient.extend_mode == ExtendMode::Repeat,
984                     );
985                 }
986                 Iframe(item) => {
987                     str_node(&mut v, "type", "iframe");
988                     u32_vec_node(&mut v, "id", &[item.pipeline_id.0, item.pipeline_id.1]);
989                 }
990                 PushStackingContext(item) => {
991                     str_node(&mut v, "type", "stacking-context");
992                     let filters = display_list.get(base.filters());
993                     write_sc(&mut v, &item.stacking_context, &scene.properties, filters);
994 
995                     let mut sub_iter = base.sub_iter();
996                     self.write_display_list(&mut v, display_list, scene, &mut sub_iter, clip_id_mapper);
997                     continue_traversal = Some(sub_iter);
998                 }
999                 Clip(item) => {
1000                     str_node(&mut v, "type", "clip");
1001                     usize_node(&mut v, "id", clip_id_mapper.add_id(item.id));
1002                     size_node(&mut v, "content-size", &base.rect().size);
1003 
1004                     let (complex_clips, complex_clip_count) = base.complex_clip();
1005                     if let Some(complex) = self.make_complex_clips_node(
1006                         complex_clip_count,
1007                         complex_clips,
1008                         display_list,
1009                     ) {
1010                         yaml_node(&mut v, "complex", complex);
1011                     }
1012 
1013                     if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
1014                         yaml_node(&mut v, "image-mask", mask_yaml);
1015                     }
1016                 }
1017                 ClipChain(item) => {
1018                     str_node(&mut v, "type", "clip-chain");
1019 
1020                     let id = ClipId::ClipChain(item.id);
1021                     u32_node(&mut v, "id", clip_id_mapper.add_id(id) as u32);
1022 
1023                     let clip_ids: Vec<u32> = display_list.get(base.clip_chain_items()).map(|clip_id| {
1024                         clip_id_mapper.map_id(&clip_id) as u32
1025                     }).collect();
1026                     u32_vec_node(&mut v, "clips", &clip_ids);
1027 
1028                     if let Some(parent) = item.parent {
1029                         let parent = ClipId::ClipChain(parent);
1030                         u32_node(&mut v, "parent", clip_id_mapper.map_id(&parent) as u32);
1031                     }
1032                 }
1033                 ScrollFrame(item) => {
1034                     str_node(&mut v, "type", "scroll-frame");
1035                     usize_node(&mut v, "id", clip_id_mapper.add_id(item.scroll_frame_id));
1036                     size_node(&mut v, "content-size", &base.rect().size);
1037                     rect_node(&mut v, "bounds", &base.local_clip().clip_rect());
1038 
1039                     let (complex_clips, complex_clip_count) = base.complex_clip();
1040                     if let Some(complex) = self.make_complex_clips_node(
1041                         complex_clip_count,
1042                         complex_clips,
1043                         display_list,
1044                     ) {
1045                         yaml_node(&mut v, "complex", complex);
1046                     }
1047 
1048                     if let Some(mask_yaml) = self.make_clip_mask_image_node(&item.image_mask) {
1049                         yaml_node(&mut v, "image-mask", mask_yaml);
1050                     }
1051                 }
1052                 StickyFrame(item) => {
1053                     str_node(&mut v, "type", "sticky-frame");
1054                     usize_node(&mut v, "id", clip_id_mapper.add_id(item.id));
1055                     rect_node(&mut v, "bounds", &base.local_clip().clip_rect());
1056 
1057                     if let Some(margin) = item.margins.top {
1058                         f32_node(&mut v, "margin-top", margin);
1059                     }
1060                     if let Some(margin) = item.margins.bottom {
1061                         f32_node(&mut v, "margin-bottom", margin);
1062                     }
1063                     if let Some(margin) = item.margins.left {
1064                         f32_node(&mut v, "margin-left", margin);
1065                     }
1066                     if let Some(margin) = item.margins.right {
1067                         f32_node(&mut v, "margin-right", margin);
1068                     }
1069 
1070                     let horizontal = vec![
1071                         Yaml::Real(item.horizontal_offset_bounds.min.to_string()),
1072                         Yaml::Real(item.horizontal_offset_bounds.max.to_string()),
1073                     ];
1074                     let vertical = vec![
1075                         Yaml::Real(item.vertical_offset_bounds.min.to_string()),
1076                         Yaml::Real(item.vertical_offset_bounds.max.to_string()),
1077                     ];
1078 
1079                     yaml_node(&mut v, "horizontal-offset-bounds", Yaml::Array(horizontal));
1080                     yaml_node(&mut v, "vertical-offset-bounds", Yaml::Array(vertical));
1081 
1082                     let applied = vec![
1083                         Yaml::Real(item.previously_applied_offset.x.to_string()),
1084                         Yaml::Real(item.previously_applied_offset.y.to_string()),
1085                     ];
1086                     yaml_node(&mut v, "previously-applied-offset", Yaml::Array(applied));
1087                 }
1088 
1089                 PopStackingContext => return,
1090                 SetGradientStops => panic!("dummy item yielded?"),
1091                 PushShadow(shadow) => {
1092                     str_node(&mut v, "type", "shadow");
1093                     vector_node(&mut v, "offset", &shadow.offset);
1094                     color_node(&mut v, "color", shadow.color);
1095                     f32_node(&mut v, "blur-radius", shadow.blur_radius);
1096                 }
1097                 PopAllShadows => {
1098                     str_node(&mut v, "type", "pop-all-shadows");
1099                 }
1100             }
1101             if !v.is_empty() {
1102                 list.push(Yaml::Hash(v));
1103             }
1104         }
1105     }
1106 
1107     fn write_display_list(
1108         &mut self,
1109         parent: &mut Table,
1110         display_list: &BuiltDisplayList,
1111         scene: &Scene,
1112         list_iterator: &mut BuiltDisplayListIter,
1113         clip_id_mapper: &mut ClipIdMapper,
1114     ) {
1115         let mut list = vec![];
1116         self.write_display_list_items(&mut list, display_list, scene, list_iterator, clip_id_mapper);
1117         parent.insert(Yaml::String("items".to_owned()), Yaml::Array(list));
1118     }
1119 }
1120 
1121 impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver {
1122     fn write_msg(&mut self, _: u32, msg: &ApiMsg) {
1123         match *msg {
1124             ApiMsg::UpdateResources(ref updates) => {
1125                 self.frame_writer.update_resources(updates);
1126             }
1127             ApiMsg::UpdateDocument(_, ref txn) => {
1128                 self.frame_writer.update_resources(&txn.resource_updates);
1129                 for doc_msg in &txn.scene_ops {
1130                     match *doc_msg {
1131                         SceneMsg::SetDisplayList {
1132                             ref epoch,
1133                             ref pipeline_id,
1134                             ref background,
1135                             ref viewport_size,
1136                             ref list_descriptor,
1137                             ..
1138                         } => {
1139                             self.frame_writer.begin_write_display_list(
1140                                 &mut self.scene,
1141                                 epoch,
1142                                 pipeline_id,
1143                                 background,
1144                                 viewport_size,
1145                                 list_descriptor,
1146                             );
1147                         }
1148                         SceneMsg::SetRootPipeline(ref pipeline_id) => {
1149                             self.scene.set_root_pipeline_id(pipeline_id.clone());
1150                         }
1151                         SceneMsg::RemovePipeline(ref pipeline_id) => {
1152                             self.scene.remove_pipeline(pipeline_id);
1153                         }
1154                         _ => {}
1155                     }
1156                 }
1157                 for doc_msg in &txn.frame_ops {
1158                     match *doc_msg {
1159                         FrameMsg::UpdateDynamicProperties(ref properties) => {
1160                             self.scene.properties.set_properties(properties);
1161                         }
1162                         _ => {}
1163                     }
1164                 }
1165             }
1166             _ => {}
1167         }
1168     }
1169 
1170     fn write_payload(&mut self, _frame: u32, data: &[u8]) {
1171         if self.frame_writer.dl_descriptor.is_some() {
1172             self.frame_writer
1173                 .finish_write_display_list(&mut self.scene, data);
1174         }
1175     }
1176 }
1177 
1178 /// This structure allows mapping both `Clip` and `ClipExternalId`
1179 /// `ClipIds` onto one set of numeric ids. This prevents ids
1180 /// from clashing in the yaml output.
1181 struct ClipIdMapper {
1182     hash_map: HashMap<ClipId, usize>,
1183     current_clip_id: usize,
1184 }
1185 
1186 impl ClipIdMapper {
1187     fn new() -> ClipIdMapper {
1188         ClipIdMapper {
1189             hash_map: HashMap::new(),
1190             current_clip_id: 1,
1191         }
1192     }
1193 
1194     fn add_id(&mut self, id: ClipId) -> usize {
1195         self.hash_map.insert(id, self.current_clip_id);
1196         self.current_clip_id += 1;
1197         self.current_clip_id - 1
1198     }
1199 
1200     fn map_id(&self, id: &ClipId) -> usize {
1201         if id.is_root_scroll_node() {
1202             return 0;
1203         }
1204         *self.hash_map.get(id).unwrap()
1205     }
1206 
1207     fn map_info(&self, info: &ClipAndScrollInfo) -> (i64, Option<i64>) {
1208         (
1209             self.map_id(&info.scroll_node_id) as i64,
1210             info.clip_node_id.map(|ref id| self.map_id(id) as i64),
1211         )
1212     }
1213 }
1214