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 use euclid::{point2, size2, rect, Box2D};
6 use std::sync::Arc;
7 use std::sync::atomic::{AtomicIsize, Ordering};
8 use std::sync::mpsc::Receiver;
9 use webrender::api::*;
10 use webrender::render_api::*;
11 use webrender::api::units::*;
12 use crate::{WindowWrapper, NotifierEvent};
13 use crate::blob;
14 use crate::reftest::{ReftestImage, ReftestImageComparison};
15 use crate::wrench::Wrench;
16 
17 pub struct RawtestHarness<'a> {
18     wrench: &'a mut Wrench,
19     rx: &'a Receiver<NotifierEvent>,
20     window: &'a mut WindowWrapper,
21 }
22 
23 
24 impl<'a> RawtestHarness<'a> {
new(wrench: &'a mut Wrench, window: &'a mut WindowWrapper, rx: &'a Receiver<NotifierEvent>) -> Self25     pub fn new(wrench: &'a mut Wrench,
26                window: &'a mut WindowWrapper,
27                rx: &'a Receiver<NotifierEvent>) -> Self {
28         RawtestHarness {
29             wrench,
30             rx,
31             window,
32         }
33     }
34 
run(mut self)35     pub fn run(mut self) {
36         self.test_hit_testing();
37         self.test_resize_image();
38         self.test_retained_blob_images_test();
39         self.test_blob_update_test();
40         self.test_blob_update_epoch_test();
41         self.test_tile_decomposition();
42         self.test_very_large_blob();
43         self.test_blob_visible_area();
44         self.test_blob_set_visible_area();
45         self.test_offscreen_blob();
46         self.test_save_restore();
47         self.test_blur_cache();
48         self.test_capture();
49         self.test_zero_height_window();
50         self.test_clear_cache();
51     }
52 
render_and_get_pixels(&mut self, window_rect: FramebufferIntRect) -> Vec<u8>53     fn render_and_get_pixels(&mut self, window_rect: FramebufferIntRect) -> Vec<u8> {
54         self.rx.recv().unwrap();
55         self.wrench.render();
56         self.wrench.renderer.read_pixels_rgba8(window_rect)
57     }
58 
compare_pixels(&self, data1: Vec<u8>, data2: Vec<u8>, size: FramebufferIntSize)59     fn compare_pixels(&self, data1: Vec<u8>, data2: Vec<u8>, size: FramebufferIntSize) {
60         let size = DeviceIntSize::new(size.width, size.height);
61         let image1 = ReftestImage {
62             data: data1,
63             size,
64         };
65         let image2 = ReftestImage {
66             data: data2,
67             size,
68         };
69 
70         match image1.compare(&image2) {
71             ReftestImageComparison::Equal => {}
72             ReftestImageComparison::NotEqual { max_difference, count_different, .. } => {
73                 let t = "rawtest";
74                 println!(
75                     "{} | {} | {}: {}, {}: {}",
76                     "REFTEST TEST-UNEXPECTED-FAIL",
77                     t,
78                     "image comparison, max difference",
79                     max_difference,
80                     "number of differing pixels",
81                     count_different
82                 );
83                 println!("REFTEST   IMAGE 1: {}", image1.create_data_uri());
84                 println!("REFTEST   IMAGE 2: {}", image2.create_data_uri());
85                 println!("REFTEST TEST-END | {}", t);
86                 panic!();
87             }
88         }
89     }
90 
submit_dl( &mut self, epoch: &mut Epoch, layout_size: LayoutSize, builder: DisplayListBuilder, mut txn: Transaction, )91     fn submit_dl(
92         &mut self,
93         epoch: &mut Epoch,
94         layout_size: LayoutSize,
95         builder: DisplayListBuilder,
96         mut txn: Transaction,
97     ) {
98         let root_background_color = Some(ColorF::new(1.0, 1.0, 1.0, 1.0));
99         txn.use_scene_builder_thread();
100 
101         txn.set_display_list(
102             *epoch,
103             root_background_color,
104             layout_size,
105             builder.finalize(),
106             false,
107         );
108         epoch.0 += 1;
109 
110         txn.generate_frame(0);
111         self.wrench.api.send_transaction(self.wrench.document_id, txn);
112     }
113 
make_common_properties(&self, clip_rect: LayoutRect) -> CommonItemProperties114     fn make_common_properties(&self, clip_rect: LayoutRect) -> CommonItemProperties {
115         let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
116         CommonItemProperties {
117             clip_rect,
118             clip_id: space_and_clip.clip_id,
119             spatial_id: space_and_clip.spatial_id,
120             flags: PrimitiveFlags::default(),
121         }
122     }
123 
make_common_properties_with_clip_and_spatial( &self, clip_rect: LayoutRect, clip_id: ClipId, spatial_id: SpatialId ) -> CommonItemProperties124     fn make_common_properties_with_clip_and_spatial(
125         &self,
126         clip_rect: LayoutRect,
127         clip_id: ClipId,
128         spatial_id: SpatialId
129     ) -> CommonItemProperties {
130         CommonItemProperties {
131             clip_rect,
132             clip_id,
133             spatial_id,
134             flags: PrimitiveFlags::default(),
135         }
136     }
137 
test_resize_image(&mut self)138     fn test_resize_image(&mut self) {
139         println!("\tresize image...");
140         // This test changes the size of an image to make it go switch back and forth
141         // between tiled and non-tiled.
142         // The resource cache should be able to handle this without crashing.
143 
144         let layout_size = LayoutSize::new(800., 800.);
145 
146         let mut txn = Transaction::new();
147         let img = self.wrench.api.generate_image_key();
148 
149         // Start with a non-tiled image.
150         txn.add_image(
151             img,
152             ImageDescriptor::new(64, 64, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
153             ImageData::new(vec![255; 64 * 64 * 4]),
154             None,
155         );
156 
157         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
158         let info = self.make_common_properties(rect(0.0, 0.0, 64.0, 64.0).to_box2d());
159 
160         builder.push_image(
161             &info,
162             info.clip_rect,
163             ImageRendering::Auto,
164             AlphaType::PremultipliedAlpha,
165             img,
166             ColorF::WHITE,
167         );
168 
169         let mut epoch = Epoch(0);
170 
171         self.submit_dl(&mut epoch, layout_size, builder, txn);
172         self.rx.recv().unwrap();
173         self.wrench.render();
174 
175         let mut txn = Transaction::new();
176         // Resize the image to something bigger than the max texture size (8196) to force tiling.
177         txn.update_image(
178             img,
179             ImageDescriptor::new(8200, 32, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
180             ImageData::new(vec![255; 8200 * 32 * 4]),
181             &DirtyRect::All,
182         );
183 
184         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
185         let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0).to_box2d());
186 
187         builder.push_image(
188             &info,
189             info.clip_rect,
190             ImageRendering::Auto,
191             AlphaType::PremultipliedAlpha,
192             img,
193             ColorF::WHITE,
194         );
195 
196         self.submit_dl(&mut epoch, layout_size, builder, txn);
197         self.rx.recv().unwrap();
198         self.wrench.render();
199 
200         let mut txn = Transaction::new();
201         // Resize back to something doesn't require tiling.
202         txn.update_image(
203             img,
204             ImageDescriptor::new(64, 64, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
205             ImageData::new(vec![64; 64 * 64 * 4]),
206             &DirtyRect::All,
207         );
208 
209         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
210         let info = self.make_common_properties(rect(0.0, 0.0, 1024.0, 1024.0).to_box2d());
211 
212         builder.push_image(
213             &info,
214             info.clip_rect,
215             ImageRendering::Auto,
216             AlphaType::PremultipliedAlpha,
217             img,
218             ColorF::WHITE,
219         );
220 
221         self.submit_dl(&mut epoch, layout_size, builder, txn);
222         self.rx.recv().unwrap();
223         self.wrench.render();
224 
225         txn = Transaction::new();
226         txn.delete_image(img);
227         self.wrench.api.send_transaction(self.wrench.document_id, txn);
228     }
229 
test_tile_decomposition(&mut self)230     fn test_tile_decomposition(&mut self) {
231         println!("\ttile decomposition...");
232         // This exposes a crash in tile decomposition
233         let layout_size = LayoutSize::new(800., 800.);
234         let mut txn = Transaction::new();
235 
236         let blob_img = self.wrench.api.generate_blob_image_key();
237         txn.add_blob_image(
238             blob_img,
239             ImageDescriptor::new(151, 56, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
240             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
241             DeviceIntRect::from_size(DeviceIntSize::new(151, 56)),
242             Some(128),
243         );
244 
245         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
246 
247         let info = self.make_common_properties(rect(448.899994, 74.0, 151.000031, 56.).to_box2d());
248 
249         // setup some malicious image size parameters
250         builder.push_repeating_image(
251             &info,
252             info.clip_rect,
253             size2(151., 56.0),
254             size2(151.0, 56.0),
255             ImageRendering::Auto,
256             AlphaType::PremultipliedAlpha,
257             blob_img.as_image(),
258             ColorF::WHITE,
259         );
260 
261         let mut epoch = Epoch(0);
262 
263         self.submit_dl(&mut epoch, layout_size, builder, txn);
264 
265         self.rx.recv().unwrap();
266         self.wrench.render();
267 
268         // Leaving a tiled blob image in the resource cache
269         // confuses the `test_capture`. TODO: remove this
270         txn = Transaction::new();
271         txn.delete_blob_image(blob_img);
272         self.wrench.api.send_transaction(self.wrench.document_id, txn);
273     }
274 
test_very_large_blob(&mut self)275     fn test_very_large_blob(&mut self) {
276         println!("\tvery large blob...");
277 
278         let window_size = self.window.get_inner_size();
279 
280         let test_size = FramebufferIntSize::new(800, 800);
281 
282         let window_rect = FramebufferIntRect::from_origin_and_size(
283             FramebufferIntPoint::new(0, window_size.height - test_size.height),
284             test_size,
285         );
286 
287         // This exposes a crash in tile decomposition
288         let layout_size = LayoutSize::new(800., 800.);
289         let mut txn = Transaction::new();
290 
291         let blob_img = self.wrench.api.generate_blob_image_key();
292         txn.add_blob_image(
293             blob_img,
294             ImageDescriptor::new(15000, 15000, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
295             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
296             DeviceIntRect::from_size(DeviceIntSize::new(15000, 15000)),
297             Some(100),
298         );
299 
300         let called = Arc::new(AtomicIsize::new(0));
301         let called_inner = Arc::clone(&called);
302 
303         self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
304             called_inner.fetch_add(1, Ordering::SeqCst);
305         });
306 
307         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
308 
309         let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
310         let clip_id = builder.define_clip_rect(
311             &root_space_and_clip,
312             rect(40., 41., 200., 201.).to_box2d(),
313         );
314 
315         let info = CommonItemProperties {
316             clip_rect: rect(0.0, 0.0, 800.0, 800.0).to_box2d(),
317             clip_id,
318             spatial_id: root_space_and_clip.spatial_id,
319             flags: PrimitiveFlags::default(),
320         };
321 
322         // setup some malicious image size parameters
323         builder.push_repeating_image(
324             &info,
325             size2(15000.0, 15000.0).into(),
326             size2(15000.0, 15000.0),
327             size2(0.0, 0.0),
328             ImageRendering::Auto,
329             AlphaType::PremultipliedAlpha,
330             blob_img.as_image(),
331             ColorF::WHITE,
332         );
333 
334         let mut epoch = Epoch(0);
335 
336         self.submit_dl(&mut epoch, layout_size, builder, txn);
337 
338         let pixels = self.render_and_get_pixels(window_rect);
339 
340         // make sure we didn't request too many blobs
341         assert!(called.load(Ordering::SeqCst) < 20);
342 
343         //use crate::png;
344         //png::save_flipped("out.png", pixels.clone(), size2(window_rect.size.width, window_rect.size.height));
345 
346         // make sure things are in the right spot
347         let w = window_rect.width() as usize;
348         let h = window_rect.height() as usize;
349         let p1 = (40 + (h - 100) * w) * 4;
350         assert_eq!(pixels[p1 + 0], 50);
351         assert_eq!(pixels[p1 + 1], 50);
352         assert_eq!(pixels[p1 + 2], 150);
353         assert_eq!(pixels[p1 + 3], 255);
354 
355         // Leaving a tiled blob image in the resource cache
356         // confuses the `test_capture`. TODO: remove this
357         txn = Transaction::new();
358         txn.delete_blob_image(blob_img);
359         self.wrench.api.send_transaction(self.wrench.document_id, txn);
360 
361         *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
362     }
363 
test_blob_visible_area(&mut self)364     fn test_blob_visible_area(&mut self) {
365         println!("\tblob visible area...");
366 
367         let window_size = self.window.get_inner_size();
368         let test_size = FramebufferIntSize::new(800, 800);
369         let window_rect = FramebufferIntRect::from_origin_and_size(
370             FramebufferIntPoint::new(0, window_size.height - test_size.height),
371             test_size,
372         );
373         let layout_size = LayoutSize::new(800.0, 800.0);
374         let mut txn = Transaction::new();
375 
376         let blob_img = self.wrench.api.generate_blob_image_key();
377         txn.add_blob_image(
378             blob_img,
379             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
380             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
381             DeviceIntRect {
382                 min: point2(50, 20),
383                 max: point2(450, 420),
384             },
385             Some(100),
386         );
387 
388         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
389 
390         let image_size = size2(400.0, 400.0);
391 
392         let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
393         let clip_id = builder.define_clip_rect(
394             &root_space_and_clip,
395             rect(-1000.0, -1000.0, 2000.0, 2000.0).to_box2d(),
396         );
397 
398         let info = CommonItemProperties {
399             clip_rect: rect(10.0, 10.0, 400.0, 400.0).to_box2d(),
400             clip_id,
401             spatial_id: root_space_and_clip.spatial_id,
402             flags: PrimitiveFlags::default(),
403         };
404 
405         builder.push_repeating_image(
406             &info,
407             info.clip_rect,
408             image_size,
409             image_size,
410             ImageRendering::Auto,
411             AlphaType::PremultipliedAlpha,
412             blob_img.as_image(),
413             ColorF::WHITE,
414         );
415         let mut epoch = Epoch(0);
416 
417         self.submit_dl(&mut epoch, layout_size, builder, txn);
418 
419         let pixels = self.render_and_get_pixels(window_rect);
420 
421         //use super::png;
422         //png::save_flipped("out.png", pixels.clone(), size2(window_rect.size.width, window_rect.size.height));
423 
424 
425         // make sure things are in the right spot
426         let w = window_rect.width() as usize;
427         let h = window_rect.height() as usize;
428         let p1 = (65 + (h - 15) * w) * 4;
429         assert_eq!(pixels[p1 + 0], 255);
430         assert_eq!(pixels[p1 + 1], 255);
431         assert_eq!(pixels[p1 + 2], 255);
432         assert_eq!(pixels[p1 + 3], 255);
433 
434         let p2 = (25 + (h - 15) * w) * 4;
435         assert_eq!(pixels[p2 + 0], 221);
436         assert_eq!(pixels[p2 + 1], 221);
437         assert_eq!(pixels[p2 + 2], 221);
438         assert_eq!(pixels[p2 + 3], 255);
439 
440         let p3 = (15 + (h - 15) * w) * 4;
441         assert_eq!(pixels[p3 + 0], 50);
442         assert_eq!(pixels[p3 + 1], 50);
443         assert_eq!(pixels[p3 + 2], 150);
444         assert_eq!(pixels[p3 + 3], 255);
445 
446         // Leaving a tiled blob image in the resource cache
447         // confuses the `test_capture`. TODO: remove this
448         txn = Transaction::new();
449         txn.delete_blob_image(blob_img);
450         self.wrench.api.send_transaction(self.wrench.document_id, txn);
451 
452         *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
453     }
454 
test_blob_set_visible_area(&mut self)455     fn test_blob_set_visible_area(&mut self) {
456         // In this test we first render a blob with a certain visible area,
457         // then change the visible area without updating the blob image.
458 
459         println!("\tblob visible area update...");
460 
461         let window_size = self.window.get_inner_size();
462         let test_size = FramebufferIntSize::new(800, 800);
463         let window_rect = FramebufferIntRect::from_origin_and_size(
464             FramebufferIntPoint::new(0, window_size.height - test_size.height),
465             test_size,
466         );
467         let layout_size = LayoutSize::new(800.0, 800.0);
468         let mut txn = Transaction::new();
469 
470         let blob_img = self.wrench.api.generate_blob_image_key();
471         txn.add_blob_image(
472             blob_img,
473             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
474             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
475             DeviceIntRect {
476                 min: point2(0, 0),
477                 max: point2(500, 500),
478             },
479             Some(128),
480         );
481 
482         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
483 
484         let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
485         let clip_id = builder.define_clip_rect(
486             &root_space_and_clip,
487             rect(-1000.0, -1000.0, 2000.0, 2000.0).to_box2d(),
488         );
489 
490         let info = CommonItemProperties {
491             clip_rect: rect(0.0, 0.0, 1000.0, 1000.0).to_box2d(),
492             clip_id,
493             spatial_id: root_space_and_clip.spatial_id,
494             flags: PrimitiveFlags::default(),
495         };
496 
497         builder.push_repeating_image(
498             &info,
499             rect(0.0, 0.0, 500.0, 500.0).to_box2d(),
500             size2(500.0, 500.0),
501             size2(500.0, 500.0),
502             ImageRendering::Auto,
503             AlphaType::PremultipliedAlpha,
504             blob_img.as_image(),
505             ColorF::WHITE,
506         );
507         let mut epoch = Epoch(0);
508 
509         // Render the first display list. We don't care about the result but we
510         // want to make sure the next display list updates an already rendered
511         // state.
512         self.submit_dl(&mut epoch, layout_size, builder, txn);
513         let _ = self.render_and_get_pixels(window_rect);
514 
515         // Now render a similar scene with an updated blob visible area.
516         // In this test we care about the fact that the visible area was updated
517         // without using update_blob_image.
518 
519         let mut txn = Transaction::new();
520 
521         txn.set_blob_image_visible_area(blob_img, DeviceIntRect {
522             min: point2(50, 50),
523             max: point2(450, 450),
524         });
525 
526         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
527 
528         let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
529         let clip_id = builder.define_clip_rect(
530             &root_space_and_clip,
531             rect(-1000.0, -1000.0, 2000.0, 2000.0).to_box2d(),
532         );
533 
534         let info = CommonItemProperties {
535             clip_rect: rect(0.0, 0.0, 1000.0, 1000.0).to_box2d(),
536             clip_id,
537             spatial_id: root_space_and_clip.spatial_id,
538             flags: PrimitiveFlags::default(),
539         };
540 
541         builder.push_repeating_image(
542             &info,
543             rect(50.0, 50.0, 400.0, 400.0).to_box2d(),
544             size2(400.0, 400.0),
545             size2(400.0, 400.0),
546             ImageRendering::Auto,
547             AlphaType::PremultipliedAlpha,
548             blob_img.as_image(),
549             ColorF::WHITE,
550         );
551 
552         self.submit_dl(&mut epoch, layout_size, builder, txn);
553         let resized_pixels = self.render_and_get_pixels(window_rect);
554 
555         // Now render the same scene with a new blob image created with the same
556         // visible area as the previous scene, without going through an update.
557 
558         let mut txn = Transaction::new();
559 
560         let blob_img2 = self.wrench.api.generate_blob_image_key();
561         txn.add_blob_image(
562             blob_img2,
563             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
564             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
565             DeviceIntRect {
566                 min: point2(50, 50),
567                 max: point2(450, 450),
568             },
569             Some(128),
570         );
571 
572         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
573 
574         let root_space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
575         let clip_id = builder.define_clip_rect(
576             &root_space_and_clip,
577             rect(-1000.0, -1000.0, 2000.0, 2000.0).to_box2d(),
578         );
579 
580         let info = CommonItemProperties {
581             clip_rect: rect(0.0, 0.0, 1000.0, 1000.0).to_box2d(),
582             clip_id,
583             spatial_id: root_space_and_clip.spatial_id,
584             flags: PrimitiveFlags::default(),
585         };
586 
587         builder.push_repeating_image(
588             &info,
589             rect(50.0, 50.0, 400.0, 400.0).to_box2d(),
590             size2(400.0, 400.0),
591             size2(400.0, 400.0),
592             ImageRendering::Auto,
593             AlphaType::PremultipliedAlpha,
594             blob_img2.as_image(),
595             ColorF::WHITE,
596         );
597         let mut epoch = Epoch(0);
598 
599         self.submit_dl(&mut epoch, layout_size, builder, txn);
600 
601         let reference_pixels = self.render_and_get_pixels(window_rect);
602 
603         assert_eq!(resized_pixels, reference_pixels);
604 
605         txn = Transaction::new();
606         txn.delete_blob_image(blob_img);
607         txn.delete_blob_image(blob_img2);
608         self.wrench.api.send_transaction(self.wrench.document_id, txn);
609     }
610 
test_offscreen_blob(&mut self)611     fn test_offscreen_blob(&mut self) {
612         println!("\toffscreen blob update...");
613 
614         let window_size = self.window.get_inner_size();
615 
616         let test_size = FramebufferIntSize::new(800, 800);
617         let window_rect = FramebufferIntRect::from_origin_and_size(
618             point2(0, window_size.height - test_size.height),
619             test_size,
620         );
621 
622         // This exposes a crash in tile decomposition
623         let mut txn = Transaction::new();
624         let layout_size = LayoutSize::new(800., 800.);
625 
626         let blob_img = self.wrench.api.generate_blob_image_key();
627         txn.add_blob_image(
628             blob_img,
629             ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
630             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
631             DeviceIntRect::from_size(size2(1510, 1510)),
632             None,
633         );
634 
635         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
636 
637         let info = self.make_common_properties(rect(0., 0.0, 1510., 1510.).to_box2d());
638 
639         let image_size = size2(1510., 1510.);
640 
641         // setup some malicious image size parameters
642         builder.push_repeating_image(
643             &info,
644             info.clip_rect,
645             image_size,
646             image_size,
647             ImageRendering::Auto,
648             AlphaType::PremultipliedAlpha,
649             blob_img.as_image(),
650             ColorF::WHITE,
651         );
652 
653         let mut epoch = Epoch(0);
654 
655         self.submit_dl(&mut epoch, layout_size, builder, txn);
656 
657         let original_pixels = self.render_and_get_pixels(window_rect);
658 
659         let mut epoch = Epoch(1);
660 
661         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
662 
663         let info = self.make_common_properties(rect(-10000., 0.0, 1510., 1510.).to_box2d());
664 
665         let image_size = size2(1510., 1510.);
666 
667         // setup some malicious image size parameters
668         builder.push_repeating_image(
669             &info,
670             info.clip_rect,
671             image_size,
672             image_size,
673             ImageRendering::Auto,
674             AlphaType::PremultipliedAlpha,
675             blob_img.as_image(),
676             ColorF::WHITE,
677         );
678 
679         self.submit_dl(&mut epoch, layout_size, builder, Transaction::new());
680 
681         let _offscreen_pixels = self.render_and_get_pixels(window_rect);
682 
683         let mut txn = Transaction::new();
684 
685         txn.update_blob_image(
686             blob_img,
687             ImageDescriptor::new(1510, 1510, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
688             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
689             DeviceIntRect::from_size(size2(1510, 1510)),
690             &Box2D { min: point2(10, 10), max: point2(110, 110) }.into(),
691         );
692 
693         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
694 
695         let info = self.make_common_properties(rect(0., 0.0, 1510., 1510.).to_box2d());
696 
697         let image_size = size2(1510., 1510.);
698 
699         // setup some malicious image size parameters
700         builder.push_repeating_image(
701             &info,
702             info.clip_rect,
703             image_size,
704             image_size,
705             ImageRendering::Auto,
706             AlphaType::PremultipliedAlpha,
707             blob_img.as_image(),
708             ColorF::WHITE,
709         );
710 
711         let mut epoch = Epoch(2);
712 
713         self.submit_dl(&mut epoch, layout_size, builder, txn);
714 
715         let pixels = self.render_and_get_pixels(window_rect);
716 
717         self.compare_pixels(original_pixels, pixels, window_rect.size());
718 
719         // Leaving a tiled blob image in the resource cache
720         // confuses the `test_capture`. TODO: remove this
721         txn = Transaction::new();
722         txn.delete_blob_image(blob_img);
723         self.wrench.api.send_transaction(self.wrench.document_id, txn);
724     }
725 
test_retained_blob_images_test(&mut self)726     fn test_retained_blob_images_test(&mut self) {
727         println!("\tretained blob images test...");
728         let blob_img;
729         let window_size = self.window.get_inner_size();
730 
731         let test_size = FramebufferIntSize::new(400, 400);
732         let window_rect = FramebufferIntRect::from_origin_and_size(
733             FramebufferIntPoint::new(0, window_size.height - test_size.height),
734             test_size,
735         );
736         let layout_size = LayoutSize::new(400., 400.);
737 
738         let mut txn = Transaction::new();
739         {
740             let api = &self.wrench.api;
741 
742             blob_img = api.generate_blob_image_key();
743             txn.add_blob_image(
744                 blob_img,
745                 ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
746                 blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
747                 DeviceIntRect::from_size(size2(500, 500)),
748                 None,
749             );
750         }
751 
752         let called = Arc::new(AtomicIsize::new(0));
753         let called_inner = Arc::clone(&called);
754 
755         self.wrench.callbacks.lock().unwrap().request = Box::new(move |_| {
756             assert_eq!(0, called_inner.fetch_add(1, Ordering::SeqCst));
757         });
758 
759         // draw the blob the first time
760         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
761         let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
762 
763         builder.push_image(
764             &info,
765             info.clip_rect,
766             ImageRendering::Auto,
767             AlphaType::PremultipliedAlpha,
768             blob_img.as_image(),
769             ColorF::WHITE,
770         );
771 
772         let mut epoch = Epoch(0);
773 
774         self.submit_dl(&mut epoch, layout_size, builder, txn);
775 
776         let pixels_first = self.render_and_get_pixels(window_rect);
777 
778         assert_eq!(1, called.load(Ordering::SeqCst));
779 
780         // draw the blob image a second time at a different location
781 
782         // make a new display list that refers to the first image
783         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
784         let info = self.make_common_properties(rect(1.0, 60.0, 200.0, 200.0).to_box2d());
785         builder.push_image(
786             &info,
787             info.clip_rect,
788             ImageRendering::Auto,
789             AlphaType::PremultipliedAlpha,
790             blob_img.as_image(),
791             ColorF::WHITE,
792         );
793 
794         let mut txn = Transaction::new();
795         txn.resource_updates.clear();
796 
797         self.submit_dl(&mut epoch, layout_size, builder, txn);
798 
799         let pixels_second = self.render_and_get_pixels(window_rect);
800 
801         // make sure we only requested once
802         assert_eq!(1, called.load(Ordering::SeqCst));
803 
804         // use png;
805         // png::save_flipped("out1.png", &pixels_first, window_rect.size);
806         // png::save_flipped("out2.png", &pixels_second, window_rect.size);
807         assert!(pixels_first != pixels_second);
808 
809         // cleanup
810         *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
811     }
812 
test_blob_update_epoch_test(&mut self)813     fn test_blob_update_epoch_test(&mut self) {
814         println!("\tblob update epoch test...");
815         let (blob_img, blob_img2);
816         let window_size = self.window.get_inner_size();
817 
818         let test_size = FramebufferIntSize::new(400, 400);
819         let window_rect = FramebufferIntRect::from_origin_and_size(
820             point2(0, window_size.height - test_size.height),
821             test_size,
822         );
823         let layout_size = LayoutSize::new(400., 400.);
824 
825         let mut txn = Transaction::new();
826         let (blob_img, blob_img2) = {
827             let api = &self.wrench.api;
828 
829             blob_img = api.generate_blob_image_key();
830             txn.add_blob_image(
831                 blob_img,
832                 ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
833                 blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
834                 DeviceIntRect::from_size(size2(500, 500)),
835                 None,
836             );
837             blob_img2 = api.generate_blob_image_key();
838             txn.add_blob_image(
839                 blob_img2,
840                 ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
841                 blob::serialize_blob(ColorU::new(80, 50, 150, 255)),
842                 DeviceIntRect::from_size(size2(500, 500)),
843                 None,
844             );
845             (blob_img, blob_img2)
846         };
847 
848         // setup some counters to count how many times each image is requested
849         let img1_requested = Arc::new(AtomicIsize::new(0));
850         let img1_requested_inner = Arc::clone(&img1_requested);
851         let img2_requested = Arc::new(AtomicIsize::new(0));
852         let img2_requested_inner = Arc::clone(&img2_requested);
853 
854         // track the number of times that the second image has been requested
855         self.wrench.callbacks.lock().unwrap().request = Box::new(move |requests| {
856             for item in requests {
857                 if item.request.key == blob_img {
858                     img1_requested_inner.fetch_add(1, Ordering::SeqCst);
859                 }
860                 if item.request.key == blob_img2 {
861                     img2_requested_inner.fetch_add(1, Ordering::SeqCst);
862                 }
863             }
864         });
865 
866         // create two blob images and draw them
867         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
868         let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
869         let info2 = self.make_common_properties(rect(200.0, 60.0, 200.0, 200.0).to_box2d());
870         let push_images = |builder: &mut DisplayListBuilder| {
871             builder.push_image(
872                 &info,
873                 info.clip_rect,
874                 ImageRendering::Auto,
875                 AlphaType::PremultipliedAlpha,
876                 blob_img.as_image(),
877                 ColorF::WHITE,
878             );
879             builder.push_image(
880                 &info2,
881                 info2.clip_rect,
882                 ImageRendering::Auto,
883                 AlphaType::PremultipliedAlpha,
884                 blob_img2.as_image(),
885                 ColorF::WHITE,
886             );
887         };
888 
889         push_images(&mut builder);
890 
891         let mut epoch = Epoch(0);
892 
893         self.submit_dl(&mut epoch, layout_size, builder, txn);
894         let _pixels_first = self.render_and_get_pixels(window_rect);
895 
896         // update and redraw both images
897         let mut txn = Transaction::new();
898         txn.update_blob_image(
899             blob_img,
900             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
901             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
902             DeviceIntRect::from_size(size2(500, 500)),
903             &Box2D { min: point2(100, 100), max: point2(200, 200) }.into(),
904         );
905         txn.update_blob_image(
906             blob_img2,
907             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
908             blob::serialize_blob(ColorU::new(59, 50, 150, 255)),
909             DeviceIntRect::from_size(size2(500, 500)),
910             &Box2D { min: point2(100, 100), max: point2(200, 200) }.into(),
911         );
912 
913         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
914         push_images(&mut builder);
915         self.submit_dl(&mut epoch, layout_size, builder, txn);
916         let _pixels_second = self.render_and_get_pixels(window_rect);
917 
918         // only update the first image
919         let mut txn = Transaction::new();
920         txn.update_blob_image(
921             blob_img,
922             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
923             blob::serialize_blob(ColorU::new(50, 150, 150, 255)),
924             DeviceIntRect::from_size(size2(500, 500)),
925             &Box2D { min: point2(200, 200), max: point2(300, 300) }.into(),
926         );
927 
928         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
929         push_images(&mut builder);
930         self.submit_dl(&mut epoch, layout_size, builder, txn);
931         let _pixels_third = self.render_and_get_pixels(window_rect);
932 
933         // the first image should be requested 3 times
934         assert_eq!(img1_requested.load(Ordering::SeqCst), 3);
935         // the second image should've been requested twice
936         assert_eq!(img2_requested.load(Ordering::SeqCst), 2);
937 
938         // cleanup
939         *self.wrench.callbacks.lock().unwrap() = blob::BlobCallbacks::new();
940     }
941 
test_blob_update_test(&mut self)942     fn test_blob_update_test(&mut self) {
943         println!("\tblob update test...");
944         let window_size = self.window.get_inner_size();
945 
946         let test_size = FramebufferIntSize::new(400, 400);
947         let window_rect = FramebufferIntRect::from_origin_and_size(
948             point2(0, window_size.height - test_size.height),
949             test_size,
950         );
951         let layout_size = LayoutSize::new(400., 400.);
952         let mut txn = Transaction::new();
953 
954         let blob_img = {
955             let img = self.wrench.api.generate_blob_image_key();
956             txn.add_blob_image(
957                 img,
958                 ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
959                 blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
960                 DeviceIntRect::from_size(size2(500, 500)),
961                 None,
962             );
963             img
964         };
965 
966         // draw the blobs the first time
967         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
968         let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
969 
970         builder.push_image(
971             &info,
972             info.clip_rect,
973             ImageRendering::Auto,
974             AlphaType::PremultipliedAlpha,
975             blob_img.as_image(),
976             ColorF::WHITE,
977         );
978 
979         let mut epoch = Epoch(0);
980 
981         self.submit_dl(&mut epoch, layout_size, builder, txn);
982         let pixels_first = self.render_and_get_pixels(window_rect);
983 
984         // draw the blob image a second time after updating it with the same color
985         let mut txn = Transaction::new();
986         txn.update_blob_image(
987             blob_img,
988             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
989             blob::serialize_blob(ColorU::new(50, 50, 150, 255)),
990             DeviceIntRect::from_size(size2(500, 500)),
991             &Box2D { min: point2(100, 100), max: point2(200, 200) }.into(),
992         );
993 
994         // make a new display list that refers to the first image
995         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
996         let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
997         builder.push_image(
998             &info,
999             info.clip_rect,
1000             ImageRendering::Auto,
1001             AlphaType::PremultipliedAlpha,
1002             blob_img.as_image(),
1003             ColorF::WHITE,
1004         );
1005 
1006         self.submit_dl(&mut epoch, layout_size, builder, txn);
1007         let pixels_second = self.render_and_get_pixels(window_rect);
1008 
1009         // draw the blob image a third time after updating it with a different color
1010         let mut txn = Transaction::new();
1011         txn.update_blob_image(
1012             blob_img,
1013             ImageDescriptor::new(500, 500, ImageFormat::BGRA8, ImageDescriptorFlags::empty()),
1014             blob::serialize_blob(ColorU::new(50, 150, 150, 255)),
1015             DeviceIntRect::from_size(size2(500, 500)),
1016             &Box2D { min: point2(200, 200), max: point2(300, 300) }.into(),
1017         );
1018 
1019         // make a new display list that refers to the first image
1020         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1021         let info = self.make_common_properties(rect(0.0, 60.0, 200.0, 200.0).to_box2d());
1022         builder.push_image(
1023             &info,
1024             info.clip_rect,
1025             ImageRendering::Auto,
1026             AlphaType::PremultipliedAlpha,
1027             blob_img.as_image(),
1028             ColorF::WHITE,
1029         );
1030 
1031         self.submit_dl(&mut epoch, layout_size, builder, txn);
1032         let pixels_third = self.render_and_get_pixels(window_rect);
1033 
1034         assert!(pixels_first != pixels_third);
1035         self.compare_pixels(pixels_first, pixels_second, window_rect.size());
1036     }
1037 
1038     // Ensures that content doing a save-restore produces the same results as not
test_save_restore(&mut self)1039     fn test_save_restore(&mut self) {
1040         println!("\tsave/restore...");
1041         let window_size = self.window.get_inner_size();
1042 
1043         let test_size = FramebufferIntSize::new(400, 400);
1044         let window_rect = FramebufferIntRect::from_origin_and_size(
1045             point2(0, window_size.height - test_size.height),
1046             test_size,
1047         );
1048         let layout_size = LayoutSize::new(400., 400.);
1049 
1050         let mut do_test = |should_try_and_fail| {
1051             let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1052 
1053             let spatial_id = SpatialId::root_scroll_node(self.wrench.root_pipeline_id);
1054             let clip_id = builder.define_clip_rect(
1055                 &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
1056                 rect(110., 120., 200., 200.).to_box2d(),
1057             );
1058             builder.push_rect(
1059                 &self.make_common_properties_with_clip_and_spatial(
1060                     rect(100., 100., 100., 100.).to_box2d(),
1061                     clip_id,
1062                     spatial_id),
1063                 rect(100., 100., 100., 100.).to_box2d(),
1064                 ColorF::new(0.0, 0.0, 1.0, 1.0),
1065             );
1066 
1067             if should_try_and_fail {
1068                 builder.save();
1069                 let clip_id = builder.define_clip_rect(
1070                     &SpaceAndClipInfo { spatial_id, clip_id },
1071                     rect(80., 80., 90., 90.).to_box2d(),
1072                 );
1073                 let space_and_clip = SpaceAndClipInfo {
1074                     spatial_id,
1075                     clip_id
1076                 };
1077                 builder.push_rect(
1078                     &self.make_common_properties_with_clip_and_spatial(
1079                         rect(110., 110., 50., 50.).to_box2d(),
1080                         clip_id,
1081                         spatial_id),
1082                     rect(110., 110., 50., 50.).to_box2d(),
1083                     ColorF::new(0.0, 1.0, 0.0, 1.0),
1084                 );
1085                 builder.push_shadow(
1086                     &space_and_clip,
1087                     Shadow {
1088                         offset: LayoutVector2D::new(1.0, 1.0),
1089                         blur_radius: 1.0,
1090                         color: ColorF::new(0.0, 0.0, 0.0, 1.0),
1091                     },
1092                     true,
1093                 );
1094                 let info = CommonItemProperties {
1095                     clip_rect: rect(110., 110., 50., 2.).to_box2d(),
1096                     clip_id,
1097                     spatial_id,
1098                     flags: PrimitiveFlags::default(),
1099                 };
1100                 builder.push_line(
1101                     &info,
1102                     &info.clip_rect,
1103                     0.0, LineOrientation::Horizontal,
1104                     &ColorF::new(0.0, 0.0, 0.0, 1.0),
1105                     LineStyle::Solid,
1106                 );
1107                 builder.restore();
1108             }
1109 
1110             {
1111                 builder.save();
1112                 let clip_id = builder.define_clip_rect(
1113                     &SpaceAndClipInfo { spatial_id, clip_id },
1114                     rect(80., 80., 100., 100.).to_box2d(),
1115                 );
1116                 builder.push_rect(
1117                     &self.make_common_properties_with_clip_and_spatial(
1118                         rect(150., 150., 100., 100.).to_box2d(),
1119                         clip_id,
1120                         spatial_id),
1121                     rect(150., 150., 100., 100.).to_box2d(),
1122                     ColorF::new(0.0, 0.0, 1.0, 1.0),
1123                 );
1124                 builder.clear_save();
1125             }
1126 
1127             let txn = Transaction::new();
1128 
1129             self.submit_dl(&mut Epoch(0), layout_size, builder, txn);
1130 
1131             self.render_and_get_pixels(window_rect)
1132         };
1133 
1134         let first = do_test(false);
1135         let second = do_test(true);
1136 
1137         self.compare_pixels(first, second, window_rect.size());
1138     }
1139 
1140     // regression test for #2769
1141     // "async scene building: cache collisions from reused picture ids"
test_blur_cache(&mut self)1142     fn test_blur_cache(&mut self) {
1143         println!("\tblur cache...");
1144         let window_size = self.window.get_inner_size();
1145 
1146         let test_size = FramebufferIntSize::new(400, 400);
1147         let window_rect = FramebufferIntRect::from_origin_and_size(
1148             point2(0, window_size.height - test_size.height),
1149             test_size,
1150         );
1151         let layout_size = LayoutSize::new(400., 400.);
1152 
1153         let mut do_test = |shadow_is_red| {
1154             let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1155             let shadow_color = if shadow_is_red {
1156                 ColorF::new(1.0, 0.0, 0.0, 1.0)
1157             } else {
1158                 ColorF::new(0.0, 1.0, 0.0, 1.0)
1159             };
1160 
1161             builder.push_shadow(
1162                 &SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id),
1163                 Shadow {
1164                     offset: LayoutVector2D::new(1.0, 1.0),
1165                     blur_radius: 1.0,
1166                     color: shadow_color,
1167                 },
1168                 true,
1169             );
1170             let info = self.make_common_properties(rect(110., 110., 50., 2.).to_box2d());
1171             builder.push_line(
1172                 &info,
1173                 &info.clip_rect,
1174                 0.0, LineOrientation::Horizontal,
1175                 &ColorF::new(0.0, 0.0, 0.0, 1.0),
1176                 LineStyle::Solid,
1177             );
1178             builder.pop_all_shadows();
1179 
1180             let txn = Transaction::new();
1181             self.submit_dl(&mut Epoch(0), layout_size, builder, txn);
1182 
1183             self.render_and_get_pixels(window_rect)
1184         };
1185 
1186         let first = do_test(false);
1187         let second = do_test(true);
1188 
1189         assert_ne!(first, second);
1190     }
1191 
test_capture(&mut self)1192     fn test_capture(&mut self) {
1193         println!("\tcapture...");
1194         let path = "../captures/test";
1195         let layout_size = LayoutSize::new(400., 400.);
1196         let dim = self.window.get_inner_size();
1197         let window_rect = FramebufferIntRect::from_origin_and_size(
1198             point2(0, dim.height - layout_size.height as i32),
1199             size2(layout_size.width as i32, layout_size.height as i32),
1200         );
1201 
1202         // 1. render some scene
1203 
1204         let mut txn = Transaction::new();
1205         let image = self.wrench.api.generate_image_key();
1206         txn.add_image(
1207             image,
1208             ImageDescriptor::new(1, 1, ImageFormat::BGRA8, ImageDescriptorFlags::IS_OPAQUE),
1209             ImageData::new(vec![0xFF, 0, 0, 0xFF]),
1210             None,
1211         );
1212 
1213         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1214 
1215         let info = self.make_common_properties(rect(300.0, 70.0, 150.0, 50.0).to_box2d());
1216         builder.push_image(
1217             &info,
1218             info.clip_rect,
1219             ImageRendering::Auto,
1220             AlphaType::PremultipliedAlpha,
1221             image,
1222             ColorF::WHITE,
1223         );
1224 
1225         let mut txn = Transaction::new();
1226 
1227         txn.set_display_list(
1228             Epoch(0),
1229             Some(ColorF::new(1.0, 1.0, 1.0, 1.0)),
1230             layout_size,
1231             builder.finalize(),
1232             false,
1233         );
1234         txn.generate_frame(0);
1235 
1236         self.wrench.api.send_transaction(self.wrench.document_id, txn);
1237 
1238         let pixels0 = self.render_and_get_pixels(window_rect);
1239 
1240         // 2. capture it
1241         self.wrench.api.save_capture(path.into(), CaptureBits::all());
1242 
1243         // 3. set a different scene
1244 
1245         builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1246 
1247         let mut txn = Transaction::new();
1248         txn.set_display_list(
1249             Epoch(1),
1250             Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
1251             layout_size,
1252             builder.finalize(),
1253             false,
1254         );
1255         self.wrench.api.send_transaction(self.wrench.document_id, txn);
1256 
1257         // 4. load the first one
1258 
1259         let mut documents = self.wrench.api.load_capture(path.into(), None);
1260         let captured = documents.swap_remove(0);
1261 
1262         // 5. render the built frame and compare
1263         let pixels1 = self.render_and_get_pixels(window_rect);
1264         self.compare_pixels(pixels0.clone(), pixels1, window_rect.size());
1265 
1266         // 6. rebuild the scene and compare again
1267         let mut txn = Transaction::new();
1268         txn.set_root_pipeline(captured.root_pipeline_id.unwrap());
1269         txn.generate_frame(0);
1270         self.wrench.api.send_transaction(captured.document_id, txn);
1271         let pixels2 = self.render_and_get_pixels(window_rect);
1272         self.compare_pixels(pixels0, pixels2, window_rect.size());
1273     }
1274 
test_zero_height_window(&mut self)1275     fn test_zero_height_window(&mut self) {
1276         println!("\tzero height test...");
1277 
1278         let layout_size = LayoutSize::new(120.0, 0.0);
1279         let window_size = DeviceIntSize::new(layout_size.width as i32, layout_size.height as i32);
1280         let doc_id = self.wrench.api.add_document(window_size);
1281 
1282         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1283         let info = self.make_common_properties(
1284             LayoutRect::from_size(LayoutSize::new(100.0, 100.0))
1285         );
1286         builder.push_rect(
1287             &info,
1288             info.clip_rect,
1289             ColorF::new(0.0, 1.0, 0.0, 1.0),
1290         );
1291 
1292         let mut txn = Transaction::new();
1293         txn.set_root_pipeline(self.wrench.root_pipeline_id);
1294         txn.set_display_list(
1295             Epoch(1),
1296             Some(ColorF::new(1.0, 0.0, 0.0, 1.0)),
1297             layout_size,
1298             builder.finalize(),
1299             false,
1300         );
1301         txn.generate_frame(0);
1302         self.wrench.api.send_transaction(doc_id, txn);
1303 
1304         // Ensure we get a notification from rendering the above, even though
1305         // there are zero visible pixels
1306         assert!(self.rx.recv().unwrap() == NotifierEvent::WakeUp { composite_needed: true });
1307     }
1308 
1309 
test_hit_testing(&mut self)1310     fn test_hit_testing(&mut self) {
1311         println!("\thit testing test...");
1312 
1313         let layout_size = LayoutSize::new(400., 400.);
1314         let mut builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1315 
1316         // Add a rectangle that covers the entire scene.
1317         let info = self.make_common_properties(LayoutRect::from_size(layout_size));
1318         builder.push_hit_test(
1319             &info,
1320             (0, 1),
1321         );
1322 
1323         // Add a simple 100x100 rectangle at 100,0.
1324         let info = self.make_common_properties(LayoutRect::from_origin_and_size(
1325             LayoutPoint::new(100., 0.),
1326             LayoutSize::new(100., 100.)
1327         ));
1328         builder.push_hit_test(
1329             &info,
1330             (0, 2),
1331         );
1332 
1333         let space_and_clip = SpaceAndClipInfo::root_scroll(self.wrench.root_pipeline_id);
1334 
1335         let make_rounded_complex_clip = |rect: &LayoutRect, radius: f32| -> ComplexClipRegion {
1336             ComplexClipRegion::new(
1337                 *rect,
1338                 BorderRadius::uniform_size(LayoutSize::new(radius, radius)),
1339                 ClipMode::Clip
1340             )
1341         };
1342 
1343         // Add a rectangle that is clipped by a rounded rect clip item.
1344         let rect = LayoutRect::from_origin_and_size(LayoutPoint::new(100., 100.), LayoutSize::new(100., 100.));
1345         let temp_clip_id = builder.define_clip_rounded_rect(
1346             &space_and_clip,
1347             make_rounded_complex_clip(&rect, 20.),
1348         );
1349         builder.push_hit_test(
1350             &CommonItemProperties {
1351                 clip_rect: rect,
1352                 clip_id: temp_clip_id,
1353                 spatial_id: space_and_clip.spatial_id,
1354                 flags: PrimitiveFlags::default(),
1355             },
1356             (0, 4),
1357         );
1358 
1359         // Add a rectangle that is clipped by a ClipChain containing a rounded rect.
1360         let rect = LayoutRect::from_origin_and_size(LayoutPoint::new(200., 100.), LayoutSize::new(100., 100.));
1361         let clip_id = builder.define_clip_rounded_rect(
1362             &space_and_clip,
1363             make_rounded_complex_clip(&rect, 20.),
1364         );
1365         let clip_chain_id = builder.define_clip_chain(None, vec![clip_id]);
1366         builder.push_hit_test(
1367             &CommonItemProperties {
1368                 clip_rect: rect,
1369                 clip_id: ClipId::ClipChain(clip_chain_id),
1370                 spatial_id: space_and_clip.spatial_id,
1371                 flags: PrimitiveFlags::default(),
1372             },
1373             (0, 5),
1374         );
1375 
1376         let mut epoch = Epoch(0);
1377         let txn = Transaction::new();
1378         self.submit_dl(&mut epoch, layout_size, builder, txn);
1379 
1380         // We render to ensure that the hit tester is up to date with the current scene.
1381         self.rx.recv().unwrap();
1382         self.wrench.render();
1383 
1384         let hit_test = |point: WorldPoint| -> HitTestResult {
1385             self.wrench.api.hit_test(
1386                 self.wrench.document_id,
1387                 None,
1388                 point,
1389             )
1390         };
1391 
1392         let assert_hit_test = |point: WorldPoint, tags: Vec<ItemTag>| {
1393             let result = hit_test(point);
1394             assert_eq!(result.items.len(), tags.len());
1395 
1396             for (hit_test_item, item_b) in result.items.iter().zip(tags.iter()) {
1397                 assert_eq!(hit_test_item.tag, *item_b);
1398             }
1399         };
1400 
1401         // We should not have any hits outside the boundaries of the scene.
1402         assert_hit_test(WorldPoint::new(-10., -10.), Vec::new());
1403         assert_hit_test(WorldPoint::new(-10., 10.), Vec::new());
1404         assert_hit_test(WorldPoint::new(450., 450.), Vec::new());
1405         assert_hit_test(WorldPoint::new(100., 450.), Vec::new());
1406 
1407         // The top left corner of the scene should only contain the background.
1408         assert_hit_test(WorldPoint::new(50., 50.), vec![(0, 1)]);
1409 
1410         // The middle of the normal rectangle should be hit.
1411         assert_hit_test(WorldPoint::new(150., 50.), vec![(0, 2), (0, 1)]);
1412 
1413         let test_rounded_rectangle = |point: WorldPoint, size: WorldSize, tag: ItemTag| {
1414             // The cut out corners of the rounded rectangle should not be hit.
1415             let top_left = point + WorldVector2D::new(5., 5.);
1416             let bottom_right = point + size.to_vector() - WorldVector2D::new(5., 5.);
1417 
1418             assert_hit_test(
1419                 WorldPoint::new(point.x + (size.width / 2.), point.y + (size.height / 2.)),
1420                 vec![tag, (0, 1)]
1421             );
1422 
1423             assert_hit_test(top_left, vec![(0, 1)]);
1424             assert_hit_test(WorldPoint::new(bottom_right.x, top_left.y), vec![(0, 1)]);
1425             assert_hit_test(WorldPoint::new(top_left.x, bottom_right.y), vec![(0, 1)]);
1426             assert_hit_test(bottom_right, vec![(0, 1)]);
1427         };
1428 
1429         test_rounded_rectangle(WorldPoint::new(100., 100.), WorldSize::new(100., 100.), (0, 4));
1430         test_rounded_rectangle(WorldPoint::new(200., 100.), WorldSize::new(100., 100.), (0, 5));
1431     }
1432 
test_clear_cache(&mut self)1433     fn test_clear_cache(&mut self) {
1434         println!("\tclear cache test...");
1435 
1436         self.wrench.api.send_message(ApiMsg::DebugCommand(DebugCommand::ClearCaches(ClearCache::all())));
1437 
1438         let layout_size = LayoutSize::new(400., 400.);
1439         let builder = DisplayListBuilder::new(self.wrench.root_pipeline_id);
1440 
1441         let txn = Transaction::new();
1442         let mut epoch = Epoch(0);
1443         self.submit_dl(&mut epoch, layout_size, builder, txn);
1444 
1445         self.rx.recv().unwrap();
1446         self.wrench.render();
1447     }
1448 }
1449