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