summaryrefslogtreecommitdiffstats
path: root/gfx/tests
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--gfx/tests/browser/browser.ini8
-rw-r--r--gfx/tests/browser/browser_native_font_cache_macos.js115
-rw-r--r--gfx/tests/browser/browser_windowless_troubleshoot_crash.js58
-rw-r--r--gfx/tests/browser/file_native_font_cache_macos.html15
-rw-r--r--gfx/tests/chrome/chrome.ini4
-rw-r--r--gfx/tests/chrome/test_device_reset.html88
-rw-r--r--gfx/tests/crashtests/1008983.html4
-rw-r--r--gfx/tests/crashtests/1011218.html17
-rw-r--r--gfx/tests/crashtests/1034403-1.html8
-rw-r--r--gfx/tests/crashtests/1056516.html15
-rw-r--r--gfx/tests/crashtests/1134549-1.svg14
-rw-r--r--gfx/tests/crashtests/1205900.html20
-rw-r--r--gfx/tests/crashtests/1216832-1.html13
-rw-r--r--gfx/tests/crashtests/1221304.html21
-rw-r--r--gfx/tests/crashtests/1225125-1.html11
-rw-r--r--gfx/tests/crashtests/1228127.html20
-rw-r--r--gfx/tests/crashtests/122875-1.html1
-rw-r--r--gfx/tests/crashtests/1229972.html17
-rw-r--r--gfx/tests/crashtests/1242811.html50
-rw-r--r--gfx/tests/crashtests/1242822.html19
-rw-r--r--gfx/tests/crashtests/1248222.html18
-rw-r--r--gfx/tests/crashtests/1278305.html20
-rw-r--r--gfx/tests/crashtests/1308394.html23
-rw-r--r--gfx/tests/crashtests/1317403-1.html18
-rw-r--r--gfx/tests/crashtests/1325159-1.html35
-rw-r--r--gfx/tests/crashtests/1331683.html2
-rw-r--r--gfx/tests/crashtests/1343666.html23
-rw-r--r--gfx/tests/crashtests/1346601-1.html5
-rw-r--r--gfx/tests/crashtests/1408078-1.html1
-rw-r--r--gfx/tests/crashtests/1464243.html13
-rw-r--r--gfx/tests/crashtests/1467847-1.html15
-rw-r--r--gfx/tests/crashtests/1468020.html63
-rw-r--r--gfx/tests/crashtests/1470437.html10
-rw-r--r--gfx/tests/crashtests/1470440.html7
-rw-r--r--gfx/tests/crashtests/1478035.html40
-rw-r--r--gfx/tests/crashtests/1490704-1.html27
-rw-r--r--gfx/tests/crashtests/1494062-blob-image-wraplist-clip.html27
-rw-r--r--gfx/tests/crashtests/1496194.html14
-rw-r--r--gfx/tests/crashtests/1501518.html16
-rw-r--r--gfx/tests/crashtests/1503986-1.html8
-rw-r--r--gfx/tests/crashtests/1505426-1.html23
-rw-r--r--gfx/tests/crashtests/1505934-1.html22
-rw-r--r--gfx/tests/crashtests/1508811.html10
-rw-r--r--gfx/tests/crashtests/1508822.html5
-rw-r--r--gfx/tests/crashtests/1509099.html7
-rw-r--r--gfx/tests/crashtests/1509123.html12
-rw-r--r--gfx/tests/crashtests/1513133.html11
-rw-r--r--gfx/tests/crashtests/1524418.html11
-rw-r--r--gfx/tests/crashtests/1529149.html23
-rw-r--r--gfx/tests/crashtests/1535657.html14
-rw-r--r--gfx/tests/crashtests/1541113.html7
-rw-r--r--gfx/tests/crashtests/1547169.html11
-rw-r--r--gfx/tests/crashtests/1566206.html28
-rw-r--r--gfx/tests/crashtests/156882-1.html205
-rw-r--r--gfx/tests/crashtests/157320-1.html11
-rw-r--r--gfx/tests/crashtests/1615091.html11
-rw-r--r--gfx/tests/crashtests/1615141.html13
-rw-r--r--gfx/tests/crashtests/1620125.html12
-rw-r--r--gfx/tests/crashtests/1640401-1.html31
-rw-r--r--gfx/tests/crashtests/1647862.html14
-rw-r--r--gfx/tests/crashtests/1647940.html7
-rw-r--r--gfx/tests/crashtests/1650989-very-large-mask.html10
-rw-r--r--gfx/tests/crashtests/1650990.html18
-rw-r--r--gfx/tests/crashtests/1651882.html24
-rw-r--r--gfx/tests/crashtests/1652750-deep-scene-stack.html24
-rw-r--r--gfx/tests/crashtests/1678938-1.html9
-rw-r--r--gfx/tests/crashtests/1679477-1.html8
-rw-r--r--gfx/tests/crashtests/1685009-1.html12
-rw-r--r--gfx/tests/crashtests/199379-1.html10
-rw-r--r--gfx/tests/crashtests/206561-1.html8
-rw-r--r--gfx/tests/crashtests/248518-1.html7
-rw-r--r--gfx/tests/crashtests/306649-1.xml1
-rw-r--r--gfx/tests/crashtests/306902-1.xml14
-rw-r--r--gfx/tests/crashtests/333861-1.html18
-rw-r--r--gfx/tests/crashtests/334735-1.html11
-rw-r--r--gfx/tests/crashtests/345576-1.html6
-rw-r--r--gfx/tests/crashtests/345629-1.html7
-rw-r--r--gfx/tests/crashtests/348462-1.html11
-rw-r--r--gfx/tests/crashtests/348462-2.html13
-rw-r--r--gfx/tests/crashtests/366643.html7
-rw-r--r--gfx/tests/crashtests/369688-1.html19
-rw-r--r--gfx/tests/crashtests/369947-1.html11
-rw-r--r--gfx/tests/crashtests/372094-1.xhtml45
-rw-r--r--gfx/tests/crashtests/376627-1.html3
-rw-r--r--gfx/tests/crashtests/377231-1.html1
-rw-r--r--gfx/tests/crashtests/377232-1.xhtml5
-rw-r--r--gfx/tests/crashtests/377461-1.xhtml16
-rw-r--r--gfx/tests/crashtests/383473-1.html8
-rw-r--r--gfx/tests/crashtests/383872-1.svg19
-rw-r--r--gfx/tests/crashtests/385228-1.svg22
-rw-r--r--gfx/tests/crashtests/385228-2.svg20
-rw-r--r--gfx/tests/crashtests/385289-1.xhtml30
-rw-r--r--gfx/tests/crashtests/385417-1.html1
-rw-r--r--gfx/tests/crashtests/385417-2.html10
-rw-r--r--gfx/tests/crashtests/385423-1.html17
-rw-r--r--gfx/tests/crashtests/385423-2.html17
-rw-r--r--gfx/tests/crashtests/385719-1.html1
-rw-r--r--gfx/tests/crashtests/389326-1-inner.xhtml29
-rw-r--r--gfx/tests/crashtests/389326-1.html9
-rw-r--r--gfx/tests/crashtests/390476.html13
-rw-r--r--gfx/tests/crashtests/393746-1.xhtml14
-rw-r--r--gfx/tests/crashtests/393749-1.html18
-rw-r--r--gfx/tests/crashtests/393822-1.html32
-rw-r--r--gfx/tests/crashtests/394246-1.html16
-rw-r--r--gfx/tests/crashtests/394246-2.html23
-rw-r--r--gfx/tests/crashtests/394384-1.html26
-rw-r--r--gfx/tests/crashtests/394751.xhtml3
-rw-r--r--gfx/tests/crashtests/395335-1.xhtml20
-rw-r--r--gfx/tests/crashtests/395458-1.html5
-rw-r--r--gfx/tests/crashtests/396321-1.svg5
-rw-r--r--gfx/tests/crashtests/398042-1.xhtml13
-rw-r--r--gfx/tests/crashtests/398042-2.xhtml13
-rw-r--r--gfx/tests/crashtests/402307-1.html10
-rw-r--r--gfx/tests/crashtests/403352.html14
-rw-r--r--gfx/tests/crashtests/403464-1.html134
-rw-r--r--gfx/tests/crashtests/404112-1.html10
-rw-r--r--gfx/tests/crashtests/404112-2.html8
-rw-r--r--gfx/tests/crashtests/405268-1.xhtml20
-rw-r--r--gfx/tests/crashtests/407761-1.html8
-rw-r--r--gfx/tests/crashtests/407842.html18
-rw-r--r--gfx/tests/crashtests/408754-1.html13
-rw-r--r--gfx/tests/crashtests/410728-1.xml14
-rw-r--r--gfx/tests/crashtests/416637-1.html5
-rw-r--r--gfx/tests/crashtests/419095-1.html20
-rw-r--r--gfx/tests/crashtests/419255-1.html4
-rw-r--r--gfx/tests/crashtests/420945-1.html4
-rw-r--r--gfx/tests/crashtests/420962-1.html4
-rw-r--r--gfx/tests/crashtests/421393-1.html14
-rw-r--r--gfx/tests/crashtests/421813-1.html4
-rw-r--r--gfx/tests/crashtests/423110-1.xhtml1
-rw-r--r--gfx/tests/crashtests/423270-1.html5
-rw-r--r--gfx/tests/crashtests/428633.html5
-rw-r--r--gfx/tests/crashtests/429899-1.html1
-rw-r--r--gfx/tests/crashtests/441360.html39
-rw-r--r--gfx/tests/crashtests/441360_data.gifbin0 -> 3016 bytes
-rw-r--r--gfx/tests/crashtests/445711.html11
-rw-r--r--gfx/tests/crashtests/463307-1.html5
-rw-r--r--gfx/tests/crashtests/467703-1.xhtml1
-rw-r--r--gfx/tests/crashtests/467873-1.html8
-rw-r--r--gfx/tests/crashtests/470418-1.html5
-rw-r--r--gfx/tests/crashtests/474410-1.html16
-rw-r--r--gfx/tests/crashtests/487549-1.html23
-rw-r--r--gfx/tests/crashtests/487549-bad_kern_table.ttfbin0 -> 18404 bytes
-rw-r--r--gfx/tests/crashtests/487724-1.html23
-rw-r--r--gfx/tests/crashtests/490777-1.html9
-rw-r--r--gfx/tests/crashtests/516512-1.html5
-rw-r--r--gfx/tests/crashtests/532726-1.html5
-rw-r--r--gfx/tests/crashtests/538065-1.html14
-rw-r--r--gfx/tests/crashtests/546870-1.html8
-rw-r--r--gfx/tests/crashtests/557348-1.html1
-rw-r--r--gfx/tests/crashtests/563740-1.html2
-rw-r--r--gfx/tests/crashtests/580100-1.html7
-rw-r--r--gfx/tests/crashtests/580100-bad_hhea_table.ttfbin0 -> 36109 bytes
-rw-r--r--gfx/tests/crashtests/580212-1.html7
-rw-r--r--gfx/tests/crashtests/580212-bad_loca_table.ttfbin0 -> 36109 bytes
-rw-r--r--gfx/tests/crashtests/580233-1.html7
-rw-r--r--gfx/tests/crashtests/580233-bad_gpos_table.ttfbin0 -> 173500 bytes
-rw-r--r--gfx/tests/crashtests/580719-1.html18
-rw-r--r--gfx/tests/crashtests/580719-bad_head_table.ttfbin0 -> 173520 bytes
-rw-r--r--gfx/tests/crashtests/593526.html1
-rw-r--r--gfx/tests/crashtests/593526.xhtml5
-rw-r--r--gfx/tests/crashtests/594654-1.xhtml5
-rw-r--r--gfx/tests/crashtests/595042-1.html1
-rw-r--r--gfx/tests/crashtests/595727-1.html23
-rw-r--r--gfx/tests/crashtests/624198.xhtml1
-rw-r--r--gfx/tests/crashtests/633322-1.html1
-rw-r--r--gfx/tests/crashtests/633453-1.html10
-rw-r--r--gfx/tests/crashtests/662467-1.html2
-rw-r--r--gfx/tests/crashtests/665218.html8
-rw-r--r--gfx/tests/crashtests/675550-1.html24
-rw-r--r--gfx/tests/crashtests/686190-1.html18
-rw-r--r--gfx/tests/crashtests/691330.svg1
-rw-r--r--gfx/tests/crashtests/691581-1.html6
-rw-r--r--gfx/tests/crashtests/693143-1.html44
-rw-r--r--gfx/tests/crashtests/696936-1.html2
-rw-r--r--gfx/tests/crashtests/699563-1.html2
-rw-r--r--gfx/tests/crashtests/710149-1.html19
-rw-r--r--gfx/tests/crashtests/746491.html17
-rw-r--r--gfx/tests/crashtests/746495.html23
-rw-r--r--gfx/tests/crashtests/746497.html20
-rw-r--r--gfx/tests/crashtests/746844.html23
-rw-r--r--gfx/tests/crashtests/746847.html19
-rw-r--r--gfx/tests/crashtests/746849.html20
-rw-r--r--gfx/tests/crashtests/746866.html25
-rw-r--r--gfx/tests/crashtests/747132.html15
-rw-r--r--gfx/tests/crashtests/747302.html16
-rw-r--r--gfx/tests/crashtests/766422-1.html6
-rw-r--r--gfx/tests/crashtests/766422-2.html17
-rw-r--r--gfx/tests/crashtests/766452-1.html6
-rw-r--r--gfx/tests/crashtests/766452-2.html6
-rw-r--r--gfx/tests/crashtests/768079-1.html4
-rw-r--r--gfx/tests/crashtests/783041-1.html63
-rw-r--r--gfx/tests/crashtests/783041-2.html73
-rw-r--r--gfx/tests/crashtests/783041-3.html71
-rw-r--r--gfx/tests/crashtests/783041-4.html82
-rw-r--r--gfx/tests/crashtests/798853.html3
-rw-r--r--gfx/tests/crashtests/805760-1.html22
-rw-r--r--gfx/tests/crashtests/805760.ttfbin0 -> 61656 bytes
-rw-r--r--gfx/tests/crashtests/812826.html4
-rw-r--r--gfx/tests/crashtests/815489.html17
-rw-r--r--gfx/tests/crashtests/836225-1.html19
-rw-r--r--gfx/tests/crashtests/839745-1.html20
-rw-r--r--gfx/tests/crashtests/856784-1.html11
-rw-r--r--gfx/tests/crashtests/893572-1.html11
-rw-r--r--gfx/tests/crashtests/893572-2.html30
-rw-r--r--gfx/tests/crashtests/893572-3.html44
-rw-r--r--gfx/tests/crashtests/893572-4.html38
-rw-r--r--gfx/tests/crashtests/895233.html29
-rw-r--r--gfx/tests/crashtests/914457-1.html9
-rw-r--r--gfx/tests/crashtests/934729.html7
-rw-r--r--gfx/tests/crashtests/944579.html1
-rw-r--r--gfx/tests/crashtests/944579.pngbin0 -> 3452 bytes
-rw-r--r--gfx/tests/crashtests/944579.svg26
-rw-r--r--gfx/tests/crashtests/950000.html40
-rw-r--r--gfx/tests/crashtests/951893.xhtml7
-rw-r--r--gfx/tests/crashtests/987013.html2
-rw-r--r--gfx/tests/crashtests/PigLatin_Plane15.ttfbin0 -> 57236 bytes
-rw-r--r--gfx/tests/crashtests/Prototype.ttfbin0 -> 29592 bytes
-rw-r--r--gfx/tests/crashtests/balinese-letter-spacing.html2
-rw-r--r--gfx/tests/crashtests/crashtests.list203
-rw-r--r--gfx/tests/crashtests/empty.html1
-rw-r--r--gfx/tests/crashtests/texture-allocator-zero-region.html9
-rw-r--r--gfx/tests/gtest/MockWidget.cpp8
-rw-r--r--gfx/tests/gtest/MockWidget.h94
-rw-r--r--gfx/tests/gtest/PolygonTestUtils.cpp163
-rw-r--r--gfx/tests/gtest/PolygonTestUtils.h40
-rw-r--r--gfx/tests/gtest/TestArena.cpp185
-rw-r--r--gfx/tests/gtest/TestArrayView.cpp20
-rw-r--r--gfx/tests/gtest/TestBSPTree.cpp694
-rw-r--r--gfx/tests/gtest/TestBufferRotation.cpp162
-rw-r--r--gfx/tests/gtest/TestColorNames.cpp91
-rw-r--r--gfx/tests/gtest/TestCompositor.cpp212
-rw-r--r--gfx/tests/gtest/TestConfigManager.cpp921
-rw-r--r--gfx/tests/gtest/TestGfxWidgets.cpp109
-rw-r--r--gfx/tests/gtest/TestLayers.cpp505
-rw-r--r--gfx/tests/gtest/TestLayers.h48
-rw-r--r--gfx/tests/gtest/TestMatrix.cpp145
-rw-r--r--gfx/tests/gtest/TestMoz2D.cpp41
-rw-r--r--gfx/tests/gtest/TestPolygon.cpp104
-rw-r--r--gfx/tests/gtest/TestQcms.cpp506
-rw-r--r--gfx/tests/gtest/TestRect.cpp645
-rw-r--r--gfx/tests/gtest/TestRegion.cpp1533
-rw-r--r--gfx/tests/gtest/TestSkipChars.cpp156
-rw-r--r--gfx/tests/gtest/TestSwizzle.cpp325
-rw-r--r--gfx/tests/gtest/TestTextureCompatibility.cpp125
-rw-r--r--gfx/tests/gtest/TestTextures.cpp307
-rw-r--r--gfx/tests/gtest/TestTreeTraversal.cpp1413
-rw-r--r--gfx/tests/gtest/TestVsync.cpp203
-rw-r--r--gfx/tests/gtest/TextureHelper.h161
-rw-r--r--gfx/tests/gtest/gfxSurfaceRefCountTest.cpp155
-rw-r--r--gfx/tests/gtest/moz.build91
-rw-r--r--gfx/tests/mochitest/mochitest.ini9
-rw-r--r--gfx/tests/mochitest/test_acceleration.html163
-rw-r--r--gfx/tests/mochitest/test_bug509244.html45
-rw-r--r--gfx/tests/mochitest/test_bug513439.html37
-rw-r--r--gfx/tests/mochitest/test_font_whitelist.html90
-rw-r--r--gfx/tests/moz.build9
-rw-r--r--gfx/tests/reftest/1086723-ref.html27
-rw-r--r--gfx/tests/reftest/1086723.html27
-rw-r--r--gfx/tests/reftest/1131264-1.svg17
-rw-r--r--gfx/tests/reftest/1143303-1.svg26
-rw-r--r--gfx/tests/reftest/1149923-ref.html28
-rw-r--r--gfx/tests/reftest/1149923.html29
-rw-r--r--gfx/tests/reftest/1419528-ref.html18
-rw-r--r--gfx/tests/reftest/1419528.html19
-rw-r--r--gfx/tests/reftest/1424673-ref.html6
-rw-r--r--gfx/tests/reftest/1424673.html39
-rw-r--r--gfx/tests/reftest/1429411-ref.html25
-rw-r--r--gfx/tests/reftest/1429411.html27
-rw-r--r--gfx/tests/reftest/1435143-ref.html22
-rw-r--r--gfx/tests/reftest/1435143.html25
-rw-r--r--gfx/tests/reftest/1444904-ref.html18
-rw-r--r--gfx/tests/reftest/1444904.html36
-rw-r--r--gfx/tests/reftest/1451168-ref.html54
-rw-r--r--gfx/tests/reftest/1451168.html56
-rw-r--r--gfx/tests/reftest/1461313-ref.html4
-rw-r--r--gfx/tests/reftest/1461313.html9
-rw-r--r--gfx/tests/reftest/1463802-ref.html22
-rw-r--r--gfx/tests/reftest/1463802.html22
-rw-r--r--gfx/tests/reftest/1474722-ref.html17
-rw-r--r--gfx/tests/reftest/1474722.html17
-rw-r--r--gfx/tests/reftest/1501195-ref.html30
-rw-r--r--gfx/tests/reftest/1501195.html31
-rw-r--r--gfx/tests/reftest/1519754-ref.html5
-rw-r--r--gfx/tests/reftest/1519754.html5
-rw-r--r--gfx/tests/reftest/1523080-ref.html7
-rw-r--r--gfx/tests/reftest/1523080.html13
-rw-r--r--gfx/tests/reftest/1523776-ref.html13
-rw-r--r--gfx/tests/reftest/1523776.html12
-rw-r--r--gfx/tests/reftest/1524261-ref.html4
-rw-r--r--gfx/tests/reftest/1524261.html10
-rw-r--r--gfx/tests/reftest/1524353-ref.html8
-rw-r--r--gfx/tests/reftest/1524353.html8
-rw-r--r--gfx/tests/reftest/1616444-same-color-different-paths-ref.html54
-rw-r--r--gfx/tests/reftest/1616444-same-color-different-paths.html92
-rw-r--r--gfx/tests/reftest/1662062-1-no-blurry.html20
-rw-r--r--gfx/tests/reftest/1662062-1-ref.html20
-rw-r--r--gfx/tests/reftest/1681610-ref.html41
-rw-r--r--gfx/tests/reftest/1681610.html40
-rw-r--r--gfx/tests/reftest/468496-1-ref.html32
-rw-r--r--gfx/tests/reftest/468496-1.html51
-rw-r--r--gfx/tests/reftest/611498-1.html19
-rw-r--r--gfx/tests/reftest/611498-ref.html6
-rw-r--r--gfx/tests/reftest/709477-1-ref.html47
-rw-r--r--gfx/tests/reftest/709477-1.html69
-rw-r--r--gfx/tests/reftest/853889-1-ref.html12
-rw-r--r--gfx/tests/reftest/853889-1.html19
-rw-r--r--gfx/tests/reftest/blacktrans.pngbin0 -> 105 bytes
-rw-r--r--gfx/tests/reftest/bug1523410-translate-scale-snap-ref.html37
-rw-r--r--gfx/tests/reftest/bug1523410-translate-scale-snap.html37
-rw-r--r--gfx/tests/reftest/bwinton.jpgbin0 -> 1110 bytes
-rw-r--r--gfx/tests/reftest/pass.svg8
-rw-r--r--gfx/tests/reftest/picture-caching-on-async-zoom.html90
-rw-r--r--gfx/tests/reftest/reftest.list29
314 files changed, 14831 insertions, 0 deletions
diff --git a/gfx/tests/browser/browser.ini b/gfx/tests/browser/browser.ini
new file mode 100644
index 0000000000..042f361a58
--- /dev/null
+++ b/gfx/tests/browser/browser.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+support-files =
+
+[browser_native_font_cache_macos.js]
+support-files =
+ file_native_font_cache_macos.html
+skip-if = (os != 'mac')
+[browser_windowless_troubleshoot_crash.js]
diff --git a/gfx/tests/browser/browser_native_font_cache_macos.js b/gfx/tests/browser/browser_native_font_cache_macos.js
new file mode 100644
index 0000000000..6bef437b62
--- /dev/null
+++ b/gfx/tests/browser/browser_native_font_cache_macos.js
@@ -0,0 +1,115 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async () => {
+ // Create a tab that loads a system font.
+ const CROSS_ORIGIN_DOMAIN = "https://example.com";
+ const TARGET_URL = `${CROSS_ORIGIN_DOMAIN}/browser/gfx/tests/browser/file_native_font_cache_macos.html`;
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: TARGET_URL },
+ async browser => {
+ await SpecialPowers.spawn(browser, [], async () => {
+ // Capture a snapshot of the tab, which will load the system font in the
+ // parent process.
+ const TARGET_WIDTH = 200;
+ const TARGET_HEIGHT = 100;
+
+ const rect = new content.window.DOMRect(
+ 0,
+ 0,
+ TARGET_WIDTH,
+ TARGET_HEIGHT
+ );
+ await SpecialPowers.snapshotContext(content.window, rect, "white");
+ });
+ }
+ );
+
+ // Now create a tab that shows the memory reporter.
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:memory" },
+ async browser => {
+ // Click the "Measure" button.
+ await SpecialPowers.spawn(browser, [], () => {
+ let measureButton = content.document.getElementById("measureButton");
+ measureButton.click();
+ });
+
+ // Copy the page text and check for an expected start with string.
+ let copiedText = await new Promise(resolve => {
+ const REPORT_TIMEOUT_MS = 15 * 1e3;
+ const EXPECTED_START_WITH = "Main Process";
+ let mostRecentTextOnClipboard = "";
+
+ SimpleTest.waitForClipboard(
+ textOnClipboard => {
+ mostRecentTextOnClipboard = textOnClipboard;
+ const gotExpected = textOnClipboard.startsWith(EXPECTED_START_WITH);
+ if (!gotExpected) {
+ // Try copying again.
+ EventUtils.synthesizeKey("A", { accelKey: true });
+ EventUtils.synthesizeKey("C", { accelKey: true });
+ }
+ return gotExpected;
+ },
+ () => {
+ EventUtils.synthesizeKey("A", { accelKey: true });
+ EventUtils.synthesizeKey("C", { accelKey: true });
+ },
+ () => {
+ resolve(mostRecentTextOnClipboard);
+ },
+ () => {
+ info(`Didn't find expected text within ${REPORT_TIMEOUT_MS}ms.`);
+ dump("*******ACTUAL*******\n");
+ dump("<<<" + mostRecentTextOnClipboard + ">>>\n");
+ dump("********************\n");
+ resolve("");
+ },
+ "text/unicode",
+ REPORT_TIMEOUT_MS
+ );
+ });
+
+ isnot(copiedText, "", "Got some text from clipboard.");
+
+ // Search the copied text for our desired pattern. Initially, check for
+ // a line with "native-font-resource-mac". If that exists, ensure that it
+ // has less than a maximum MB. If that doesn't exist, check instead for
+ // a line with "gfx" before the "Other Measurements" section. If that
+ // exists, it is tested against the same MB limit. If it doesn't exist,
+ // that is an indication that "gfx" doesn't occur in the first section
+ // "Explicit Allocations', and therefore isn't holding memory at all.
+ const MB_EXCLUSIVE_MAX = 20;
+ const nfrm_line = /^.*?(\d+)\.\d+ MB.*-- native-font-resource-mac/m;
+ const nfrm_match = nfrm_line.exec(copiedText);
+ if (nfrm_match) {
+ const nfrm_mb = nfrm_match[1];
+ ok(
+ nfrm_mb < MB_EXCLUSIVE_MAX,
+ `native-font-resource-mac ${nfrm_mb} MB should be less than ${MB_EXCLUSIVE_MAX} MB.`
+ );
+ } else {
+ // Figure out where the "Other Measurements" section begins.
+ const om_line = /^Other Measurements$/m;
+ const om_match = om_line.exec(copiedText);
+
+ // Find the first gfx line, and if it occurs before the "Other
+ // Measurements" section, check its size.
+ const gfx_line = /^.*?(\d+)\.\d+ MB.*-- gfx/m;
+ const gfx_match = gfx_line.exec(copiedText);
+ if (gfx_match && gfx_match.index < om_match.index) {
+ const gfx_mb = gfx_match[1];
+ ok(
+ gfx_mb < MB_EXCLUSIVE_MAX,
+ `Explicit Allocations gfx ${gfx_mb} MB should be less than ${MB_EXCLUSIVE_MAX} MB.`
+ );
+ } else {
+ ok(true, "Explicit Allocations gfx is not listed.");
+ }
+ }
+ }
+ );
+});
diff --git a/gfx/tests/browser/browser_windowless_troubleshoot_crash.js b/gfx/tests/browser/browser_windowless_troubleshoot_crash.js
new file mode 100644
index 0000000000..9afce41163
--- /dev/null
+++ b/gfx/tests/browser/browser_windowless_troubleshoot_crash.js
@@ -0,0 +1,58 @@
+add_task(async function test_windowlessBrowserTroubleshootCrash() {
+ let webNav = Services.appShell.createWindowlessBrowser(false);
+
+ let onLoaded = new Promise((resolve, reject) => {
+ let docShell = webNav.docShell;
+ let listener = {
+ observe(contentWindow, topic, data) {
+ let observedDocShell = contentWindow.docShell.sameTypeRootTreeItem.QueryInterface(
+ Ci.nsIDocShell
+ );
+ if (docShell === observedDocShell) {
+ Services.obs.removeObserver(
+ listener,
+ "content-document-global-created"
+ );
+ resolve();
+ }
+ },
+ };
+ Services.obs.addObserver(listener, "content-document-global-created");
+ });
+ let loadURIOptions = {
+ triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({}),
+ };
+ webNav.loadURI("about:blank", loadURIOptions);
+
+ await onLoaded;
+
+ let winUtils = webNav.document.defaultView.windowUtils;
+ try {
+ is(
+ winUtils.layerManagerType,
+ "Basic",
+ "windowless browser's layerManagerType should be 'Basic'"
+ );
+ } catch (e) {
+ // The windowless browser may not have a layermanager at all yet, and that's ok.
+ // The troubleshooting code similarly skips over windows with no layer managers.
+ }
+ ok(true, "not crashed");
+
+ var Troubleshoot = ChromeUtils.import(
+ "resource://gre/modules/Troubleshoot.jsm",
+ {}
+ ).Troubleshoot;
+ var data = await new Promise((resolve, reject) => {
+ Troubleshoot.snapshot(data => {
+ resolve(data);
+ });
+ });
+
+ ok(
+ data.graphics.windowLayerManagerType !== "None",
+ "windowless browser window should not set windowLayerManagerType to 'None'"
+ );
+
+ webNav.close();
+});
diff --git a/gfx/tests/browser/file_native_font_cache_macos.html b/gfx/tests/browser/file_native_font_cache_macos.html
new file mode 100644
index 0000000000..84692a4ca7
--- /dev/null
+++ b/gfx/tests/browser/file_native_font_cache_macos.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8"/>
+<style>
+html {
+ font-size: 40px;
+ font-family: apple color emoji;
+}
+</style>
+</head>
+<body>
+🔍🍔🔥
+</body>
+</html>
diff --git a/gfx/tests/chrome/chrome.ini b/gfx/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..6c5ba90a0d
--- /dev/null
+++ b/gfx/tests/chrome/chrome.ini
@@ -0,0 +1,4 @@
+[DEFAULT]
+
+[test_device_reset.html]
+subsuite = gpu
diff --git a/gfx/tests/chrome/test_device_reset.html b/gfx/tests/chrome/test_device_reset.html
new file mode 100644
index 0000000000..19fd2d365e
--- /dev/null
+++ b/gfx/tests/chrome/test_device_reset.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1274663
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test device reset</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1274663">Mozilla Bug 1274663</a>
+ <script>
+ var importObj = {};
+
+ var windows = SpecialPowers.Services.ww.getWindowEnumerator();
+ var windowutils;
+ while (windows.hasMoreElements()) {
+ windowutils = windows.getNext().windowUtils;
+ }
+
+ const PAGE_WIDTH = 200;
+ const PAGE_HEIGHT = 200;
+
+ // Helper functions
+
+ function testCompositor(ctx) {
+ takeWindowSnapshot(ctx);
+ var testPassed = true;
+
+ if (!verifyCanvasRendering(ctx)) {
+ testPassed = false;
+ }
+
+ return testPassed;
+ }
+
+ function testPixel(ctx, x, y, r, g, b, a, fuzz) {
+ var data = ctx.getImageData(x, y, 1, 1);
+
+ if (Math.abs(data.data[0] - r) <= fuzz &&
+ Math.abs(data.data[1] - g) <= fuzz &&
+ Math.abs(data.data[2] - b) <= fuzz &&
+ Math.abs(data.data[3] - a) <= fuzz) {
+ return true;
+ }
+ return false;
+ }
+
+ function verifyCanvasRendering(ctx) {
+ return testPixel(ctx, 20, 20, 140, 25, 86, 255, 0);
+ }
+
+ function takeWindowSnapshot(ctx) {
+ var flags = ctx.DRAWWINDOW_DRAW_CARET | ctx.DRAWWINDOW_DRAW_VIEW | ctx.DRAWWINDOW_USE_WIDGET_LAYERS;
+ ctx.drawWindow(window, 0, 0, PAGE_WIDTH, PAGE_HEIGHT, "rgb(140,25,86)", flags);
+ }
+
+ function createCanvas() {
+ let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+
+ canvas.setAttribute("width", PAGE_WIDTH + "px");
+ canvas.setAttribute("height", PAGE_HEIGHT + "px");
+
+ return canvas;
+ }
+
+ // Test runner code
+ windowutils.triggerDeviceReset();
+
+ SimpleTest.waitForExplicitFinish();
+ window.addEventListener("MozAfterPaint", function paintHandle(e) {
+ runCanvasTest();
+ window.removeEventListener("MozAfterPaint", paintHandle);
+ });
+
+ function runCanvasTest() {
+ const canvas = createCanvas();
+ const ctx = canvas.getContext("2d");
+ document.body.appendChild(canvas);
+
+ ok(testCompositor(ctx), "Canvas did not get rendered after device reset");
+ SimpleTest.finish();
+ }
+ </script>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/1008983.html b/gfx/tests/crashtests/1008983.html
new file mode 100644
index 0000000000..724264c68c
--- /dev/null
+++ b/gfx/tests/crashtests/1008983.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+<div style="mix-blend-mode: darken;">Text</div>
+</body>
diff --git a/gfx/tests/crashtests/1011218.html b/gfx/tests/crashtests/1011218.html
new file mode 100644
index 0000000000..c58892ee51
--- /dev/null
+++ b/gfx/tests/crashtests/1011218.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+function boom()
+{
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.setLineDash([-1]);
+ ctx.isPointInStroke(0, 0);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/1034403-1.html b/gfx/tests/crashtests/1034403-1.html
new file mode 100644
index 0000000000..714994d3e3
--- /dev/null
+++ b/gfx/tests/crashtests/1034403-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body style="font-variant: small-caps; font-family: 'Times New Roman';">
+<div>x&#xE0131;</div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/1056516.html b/gfx/tests/crashtests/1056516.html
new file mode 100644
index 0000000000..77891ae26f
--- /dev/null
+++ b/gfx/tests/crashtests/1056516.html
@@ -0,0 +1,15 @@
+<!-- crashtest for bug 1056516 -->
+<!-- This test is imported from the fuzzy-test in Bug 1346248-->
+<html>
+ <head>
+ <script>
+ o1 = document.createElement('acronym');
+ o2 = document.createElement('style');
+ o1.innerText = "溥¡‰à§†ïµ‚䢤–ç²‚â¦¼ëˆ¬ï¿áŽâŽ®ï¿½ë¨¸å¾°î¿ 蝫㱹ⳓ䳟൪꥛倾緮�됅 嶨ﺉ鵕☋⬹"; {};
+ document.documentElement.appendChild(o1);
+ document.head.appendChild(o2);
+ document.styleSheets[0].insertRule("* { word-spacing: calc(1em); width: calc(1% + 100%); hyphens: auto; }", 0);
+ </script>
+ </head>
+ <body></body>
+</html>
diff --git a/gfx/tests/crashtests/1134549-1.svg b/gfx/tests/crashtests/1134549-1.svg
new file mode 100644
index 0000000000..1d0d5484a8
--- /dev/null
+++ b/gfx/tests/crashtests/1134549-1.svg
@@ -0,0 +1,14 @@
+<svg xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="400" height="400"
+ viewBox="694400 -179730 8000 8000">
+
+ <defs>
+ <path id="tp_crash" d="M 698430.938,-174861.922 C 699068.125,-175493.781 699593.562,-176022.531 699499,-177727" />
+ </defs>
+
+ <text font-size="4000">
+ <textPath xlink:href="#tp_crash">Eisack</textPath>
+ </text>
+ <use xlink:href="#tp_crash" fill="none" stroke="green" stroke-width="200"></use>
+</svg>
diff --git a/gfx/tests/crashtests/1205900.html b/gfx/tests/crashtests/1205900.html
new file mode 100644
index 0000000000..5e1f47cae5
--- /dev/null
+++ b/gfx/tests/crashtests/1205900.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+
+<body>
+<canvas id="canvas0"></canvas>
+
+<script>
+ var canvas0=document.getElementById("canvas0");
+ var ctx=canvas0.getContext("2d");
+ canvas0.addEventListener("DOMAttrModified",
+ function(event) {
+ canvas0.toBlob(function(){},"image/jpeg",1);
+ }, true);
+ canvas0.setAttribute("height",470)
+</script>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/1216832-1.html b/gfx/tests/crashtests/1216832-1.html
new file mode 100644
index 0000000000..0f02a9280b
--- /dev/null
+++ b/gfx/tests/crashtests/1216832-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta charset="UTF-8">
+</head>
+<body>
+ <div style="transform-style: preserve-3d; border-bottom-style: outset; width: 4787550px;">
+ <div style="margin-left: 4787550px;"></div>
+ <div style="will-change: contents, transform;"></div>
+ </div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/1221304.html b/gfx/tests/crashtests/1221304.html
new file mode 100644
index 0000000000..9fd95923b7
--- /dev/null
+++ b/gfx/tests/crashtests/1221304.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.arc(32, 2, 64, 512, 1024, false);
+ ctx.scale(256, 16);
+ ctx.transform(32, 64, 16, 256, 0.5000140400370615, -0.6268189185361422);
+ ctx.scale(2, 0.33614443599622335);
+ ctx.transform(16, -64, 512, 0.5546715728833042, 4, -0.09260164319830985);
+ ctx.isPointInStroke(1024, 2048);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/1225125-1.html b/gfx/tests/crashtests/1225125-1.html
new file mode 100644
index 0000000000..6632dff283
--- /dev/null
+++ b/gfx/tests/crashtests/1225125-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<div style="mix-blend-mode: saturation; margin-right: 1187px; transform: translateX(2px);">
+ <div style="border-style: outset; mix-blend-mode: color-dodge; display: inherit; padding: 1760px; float: right;"></div>
+ <div style="height: 2000px; overflow: auto;"></div>
+</div>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/1228127.html b/gfx/tests/crashtests/1228127.html
new file mode 100644
index 0000000000..5bd4d1f480
--- /dev/null
+++ b/gfx/tests/crashtests/1228127.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.scale(49572022, 5988146080);
+ ctx.rotate(243.375);
+ ctx.transform(55.3, 963, 130.57142857142858, 26, 48.4, 13.666666666666666);
+ ctx.lineWidth = 212.5;
+ ctx.strokeText("abcdefghijklmnopqrstuvw", 0, 0, 329.3333333333333);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/122875-1.html b/gfx/tests/crashtests/122875-1.html
new file mode 100644
index 0000000000..6241666b03
--- /dev/null
+++ b/gfx/tests/crashtests/122875-1.html
@@ -0,0 +1 @@
+<html> <head> <meta http-equiv="content-type" content="text/html; charset=Shift_JIS"> </head> <body> @@ </body> </html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1229972.html b/gfx/tests/crashtests/1229972.html
new file mode 100644
index 0000000000..5d0818b4db
--- /dev/null
+++ b/gfx/tests/crashtests/1229972.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.scale(2, 0);
+ ctx.fillText("AB", 0, 0, 1);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/1242811.html b/gfx/tests/crashtests/1242811.html
new file mode 100644
index 0000000000..feb72a8ec7
--- /dev/null
+++ b/gfx/tests/crashtests/1242811.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+ var canvasA = document.createElement('canvas');
+ var ctxA = (canvasA.getContext('2d'));
+
+ var canvasB = document.createElement('canvas');
+ var ctxB = (canvasB.getContext('2d'));
+
+ var canvasC = document.createElement('canvas');
+ var ctxC = (canvasC.getContext('2d'));
+
+ var canvasD = document.createElement('canvas');
+ document.body.appendChild(canvasD);
+ var ctxD = (canvasD.getContext('2d'));
+
+ ctxB.setTransform(0.11269837969744075, 0.5254615427752635, 0.2, 4, 0.8446743569440848, 0.144);
+ ctxB.strokeText("DDDD",2,16);
+
+ ctxC.setTransform(8,0.10555935723370857,-2,0.35743616669379086,8,0.6599638731358489);
+ ctxC.strokeText("CCCC",0.309,0.7226920560287992);
+
+ ctxD.setTransform(-6, -0.40335219665508537, -32, -16, 8, 0.06330341125449711);
+ ctxD.fillText("AAAAAAAA",0.749783522856837,3);
+
+ setTimeout(function() {
+
+ canvasC.height = 400;
+
+ setTimeout(function() {
+
+ ctxA.setTransform(-0.2578973174095154, 0, 8, -41.83665466308594, 4.02983283996582, 1041.4510498046875);
+ ctxA.fillText("AA",16,0.417);
+
+ ctxC.setTransform(0.22722245734818802, 0.04656468332897981, 8, 8, 2, 256);
+ ctxC.strokeText("QQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQQ",2048,-0.903);
+
+ }, 0);
+
+ }, 0);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/1242822.html b/gfx/tests/crashtests/1242822.html
new file mode 100644
index 0000000000..b993339445
--- /dev/null
+++ b/gfx/tests/crashtests/1242822.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.shadowBlur = 1024;
+ ctx.shadowColor = "red"
+ ctx.transform(16384,-0.6842606067657471,32768,-1.0760749578475952,2048,-1.9289406538009644);
+ ctx.strokeText("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",0.691153419364978,0.7370920539221475);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/1248222.html b/gfx/tests/crashtests/1248222.html
new file mode 100644
index 0000000000..13cd7f4d56
--- /dev/null
+++ b/gfx/tests/crashtests/1248222.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom() {
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.setTransform(0.1393438068077303, 0.25634277175561127, 512, 32, 0.5548660053300825, 8);
+ ctx.transform(16, 1024, -0.958697722982312, 6, 32, 256);
+ ctx.fillRect(0.20365260220217812, -0.8620547922006936, 1, 0.6929549739446852);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/1278305.html b/gfx/tests/crashtests/1278305.html
new file mode 100644
index 0000000000..a068cf35f3
--- /dev/null
+++ b/gfx/tests/crashtests/1278305.html
@@ -0,0 +1,20 @@
+<html>
+<head>
+<style>
+body {
+ mask: url(#mymask);
+}
+div::after {
+ content: counter(n);
+}
+</style>
+<script>
+window.onload = function(){
+ document.getElementsByTagName('body')[0].animate(
+ [{"transform": "skewy(11rad)"},
+ {"transform": "rotatex(0.125turn)"}],
+ {"fill":"forwards", "iterations": 0.75, "duration": 1});
+};
+</script></head>
+<body><div></div></body>
+</html>
diff --git a/gfx/tests/crashtests/1308394.html b/gfx/tests/crashtests/1308394.html
new file mode 100644
index 0000000000..3680878ea9
--- /dev/null
+++ b/gfx/tests/crashtests/1308394.html
@@ -0,0 +1,23 @@
+<html class="reftest-paged"><head>
+<svg xmlns="http://www.w3.org/2000/svg" width="90" height="20">
+ <linearGradient id="b" x2="0" y2="100%">
+ <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
+ <stop offset="1" stop-opacity=".1"/>
+ </linearGradient>
+ <mask id="a">
+ <rect width="90" height="20" rx="3" fill="#fff"/>
+ </mask>
+ <g mask="url(#a)">
+ <path fill="#555" d="M0 0h33v20H0z"/>
+ <path fill="#007ec6" d="M33 0h57v20H33z"/>
+ <path fill="url(#b)" d="M0 0h90v20H0z"/>
+ </g>
+ <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
+ <text x="16.5" y="15" fill="#010101" fill-opacity=".3">AUR</text>
+ <text x="16.5" y="14">AUR</text>
+ <text x="60.5" y="15" fill="#010101" fill-opacity=".3">v2.0.4-1</text>
+ <text x="60.5" y="14">v2.0.4-1</text>
+ </g>
+</svg>
+</head>
+<body>
diff --git a/gfx/tests/crashtests/1317403-1.html b/gfx/tests/crashtests/1317403-1.html
new file mode 100644
index 0000000000..1a972c0e19
--- /dev/null
+++ b/gfx/tests/crashtests/1317403-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<style>
+#o {
+ padding: 27660vw;
+ outline: thick dotted;
+ mask: subtract url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=), linear-gradient(#FFF, #555);
+}
+</style>
+<script>
+document.addEventListener("DOMContentLoaded", function(){
+ o.appendChild(document.createElement("div"));
+ o.appendChild(document.createElement("frame"));
+ o.appendChild(document.createElement("div"));
+});
+</script>
+<span id=o />
+</html>
diff --git a/gfx/tests/crashtests/1325159-1.html b/gfx/tests/crashtests/1325159-1.html
new file mode 100644
index 0000000000..814d4a6bca
--- /dev/null
+++ b/gfx/tests/crashtests/1325159-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+#o_1 {
+ position: absolute;
+ top: 0px;
+ width: 100px;
+ height: 100px;
+ background: red;
+}
+#o_2 {
+ height: 10000px;
+}
+</style>
+<script>
+function boom(){
+ let doc = document.documentElement;
+ o_2.style.MozBorderEndStyle = "dotted";
+ doc.style.MozPerspective = "24.5pt";
+ o_0.style.MozTransformStyle = "preserve-3d";
+ doc.style.overflow = "scroll hidden";
+ doc.style.textOverflow = "''";
+ o_0.style.offsetInlineStart = "calc(3*25px)";
+ doc.style.paddingTop = "calc(67108864%)";
+ doc.style.width = "3e-0%";
+ o_0.style.display = "-moz-stack";
+ o_0.style.position = "relative";
+}
+addEventListener("DOMContentLoaded", boom);
+</script>
+</head>
+<body id=o_0><div id=o_1></div><div id=o_2></div></body>
+</html>
diff --git a/gfx/tests/crashtests/1331683.html b/gfx/tests/crashtests/1331683.html
new file mode 100644
index 0000000000..f8718d45d3
--- /dev/null
+++ b/gfx/tests/crashtests/1331683.html
@@ -0,0 +1,2 @@
+<!-- crashtest for bug 1331683 on Mac OS X 10.9/10.10 -->
+<div style="font-family:Skia">hello world</div>
diff --git a/gfx/tests/crashtests/1343666.html b/gfx/tests/crashtests/1343666.html
new file mode 100644
index 0000000000..a448dad09c
--- /dev/null
+++ b/gfx/tests/crashtests/1343666.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+<head>
+<meta charset="UTF-8">
+<script>
+
+function f()
+{
+ finish();
+}
+
+window.onload = function() {
+ let a = window.open("empty.html", null, "width=300,height=300");
+ setTimeout(function(){
+ a.close();
+ a.addEventListener("vrdisplayconnect", function(){});
+ window.close();
+ document.documentElement.removeAttribute("class");
+ }, 0);
+};
+
+</script>
+</head>
+</html>
diff --git a/gfx/tests/crashtests/1346601-1.html b/gfx/tests/crashtests/1346601-1.html
new file mode 100644
index 0000000000..a10780ded4
--- /dev/null
+++ b/gfx/tests/crashtests/1346601-1.html
@@ -0,0 +1,5 @@
+<svg>
+<filter id='a' width='181.412449629' primitiveUnits='objectBoundingBox'>
+<feConvolveMatrix kernelMatrix='0 1 1 1 0 0 0 0 0' kernelUnitLength='178'/>
+</filter>
+<polyline filter='url(#a)' points='47.2081,146 175.4644,1 260,191 4,0 85,176 88,248 16385,255.891 130,183 71,16'/>
diff --git a/gfx/tests/crashtests/1408078-1.html b/gfx/tests/crashtests/1408078-1.html
new file mode 100644
index 0000000000..1d2594bb2e
--- /dev/null
+++ b/gfx/tests/crashtests/1408078-1.html
@@ -0,0 +1 @@
+<div style="font-variant:small-caps">Don't crash&#x1f600;!
diff --git a/gfx/tests/crashtests/1464243.html b/gfx/tests/crashtests/1464243.html
new file mode 100644
index 0000000000..7072478960
--- /dev/null
+++ b/gfx/tests/crashtests/1464243.html
@@ -0,0 +1,13 @@
+<style>
+:not(basefont) {
+ box-shadow: 0 0 8px -moz-win-mediatext;
+ transform: scaley(56);
+}
+.cl {
+ padding-top: 16vw;
+}
+</style>
+<menu>
+<menu class="cl"></menu>
+<menu style="-webkit-perspective: 1px">
+<menu>
diff --git a/gfx/tests/crashtests/1467847-1.html b/gfx/tests/crashtests/1467847-1.html
new file mode 100644
index 0000000000..ed7d1dcf43
--- /dev/null
+++ b/gfx/tests/crashtests/1467847-1.html
@@ -0,0 +1,15 @@
+<html>
+<style>
+:root{
+transform-style:preserve-3d;
+}
+</style>
+<script>
+addEventListener("DOMContentLoaded", () => {
+ let s = document.createElement("fieldset")
+ document.getElementsByTagName("body")[0].appendChild(s)
+ s.animate([{ "transform": "matrix3d(258,8,296,626,168,58,272,151,47,-0,90,-101,116,-119,65,182) translate3d(199ch,238in,47q)" }], 1000)
+})
+</script>
+<body></body>
+</html>
diff --git a/gfx/tests/crashtests/1468020.html b/gfx/tests/crashtests/1468020.html
new file mode 100644
index 0000000000..6150d20013
--- /dev/null
+++ b/gfx/tests/crashtests/1468020.html
@@ -0,0 +1,63 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<meta charset="utf-8">
+<title>Testcase</title>
+<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
+<style>
+.homePage .entry svg .letters {
+ position: relative;
+ perspective: 1200px;
+}
+.entryletter {
+ display: block;
+ transform-origin: center;
+ backface-visibility: hidden;
+ transform-origin: center;
+}
+</style>
+</head>
+<body class="homePage">
+ <section class="entry">
+ <div class="svgWrap">
+ <svg id="entryLogo" data-name="entryLogo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 436 250">
+ <g class="vodafone-g">
+ <path d="M152.61,21.4q0.38-5.27,1-6.1a1.8,1.8,0,0,1,1.51-.5l1.82,0,1.57,0h0.38a5.58,5.58,0,0,1,1.25.1,0.57,0.57,0,0,1,.42.65,52.09,52.09,0,0,1-.9,6q-0.9,5-2.45,13.94-0.42,2.47-.65,4.22t-0.46,2.93a5.06,5.06,0,0,1-.59,1.71,1.19,1.19,0,0,1-1.07.54,2.92,2.92,0,0,1-.56-0.06,3.3,3.3,0,0,0-.65-0.06l-1.46,0-0.88,0-0.73,0-0.73,0-0.75,0-0.54,0a1,1,0,0,1-.84-0.33,2.63,2.63,0,0,1-.48-1.07,15.51,15.51,0,0,1-.33-1.88q-0.15-1.15-.4-2.78-0.54-3.39-1.38-7.17t-1.55-8.21q-0.59-3.55-1-5.43a21.32,21.32,0,0,1-.38-2.17,0.85,0.85,0,0,1,.4-0.86,3.6,3.6,0,0,1,1.4-.19h4.18a2.71,2.71,0,0,1,.92.13,0.76,0.76,0,0,1,.46.5,47.65,47.65,0,0,1,.75,6.17q0.46,5.25,1,14.95h0.54Q152.24,26.67,152.61,21.4Z" style="fill: #ed1d24"></path>
+ <path d="M162.86,32.81q0-3.39.21-5.66a7.24,7.24,0,0,1,1.17-3.62,6.2,6.2,0,0,1,2.61-2.32,9,9,0,0,1,3.62-.65,8.34,8.34,0,0,1,3.55.67,5.18,5.18,0,0,1,2.26,2,9.86,9.86,0,0,1,1.19,3.47,29.9,29.9,0,0,1,.36,4.93q0,3.6-.12,6a9.69,9.69,0,0,1-.79,3.74,6.59,6.59,0,0,1-2.63,3,8.25,8.25,0,0,1-4.1.92,7.61,7.61,0,0,1-3.62-.75,5.49,5.49,0,0,1-2.24-2.3,11.38,11.38,0,0,1-1.15-3.91A42.7,42.7,0,0,1,162.86,32.81Zm6,4.1q0,1.92,1.38,1.92a1.41,1.41,0,0,0,1.23-.5,2.89,2.89,0,0,0,.36-1.63V29.76a5.79,5.79,0,0,0-.27-2.07,1.31,1.31,0,0,0-1.36-.65q-1.34,0-1.34,2v7.86Z" style="fill: #ed1d24"></path>
+ <path d="M191,16.72c0-.42,0-0.77,0-1a1.32,1.32,0,0,1,.21-0.65,1,1,0,0,1,.54-0.33,3.91,3.91,0,0,1,1-.1h2.63a9.19,9.19,0,0,1,1,0,0.71,0.71,0,0,1,.5.23,1.17,1.17,0,0,1,.19.59q0,0.4,0,1.11V40.72q0,0.71,0,1.36c0,0.43,0,.84,0,1.23a1.61,1.61,0,0,1-.42,1.3,2.49,2.49,0,0,1-1.51.34h-2.47a1.78,1.78,0,0,1-1.19-.27,2.31,2.31,0,0,1-.4-1.23,5.46,5.46,0,0,1-1.69,1.32,4.58,4.58,0,0,1-1.94.4,4.19,4.19,0,0,1-4.14-3.07q-1.17-3.07-1.17-9.34t1.32-9.16A4.69,4.69,0,0,1,191,21.65V16.72Zm-2.68,18.94a8.39,8.39,0,0,0,.25,2.4,1.13,1.13,0,0,0,1.21.77q1.5,0,1.5-2.51V30.1a9,9,0,0,0-.23-2.36,1.16,1.16,0,0,0-1.28-.77,1.14,1.14,0,0,0-1.21.67,6.37,6.37,0,0,0-.25,2.09v5.94Z" style="fill: #ed1d24"></path>
+ <path d="M210.51,43.39a3,3,0,0,1-3.18,1.92,4.32,4.32,0,0,1-2.21-.59A5.09,5.09,0,0,1,203.41,43a9.39,9.39,0,0,1-1.13-2.7,13.94,13.94,0,0,1-.42-3.55,14.33,14.33,0,0,1,.44-3.72,8.64,8.64,0,0,1,1.23-2.76,5.82,5.82,0,0,1,1.84-1.73,4.5,4.5,0,0,1,2.3-.61,4,4,0,0,1,1.53.27,3,3,0,0,0,1.11.27q0.59,0,.59-0.79a1.79,1.79,0,0,0-.67-1.46,2.91,2.91,0,0,0-1.88-.54,6,6,0,0,0-2.26.38,4.15,4.15,0,0,1-1.46.38,0.86,0.86,0,0,1-.8-0.33,2,2,0,0,1-.21-1V23a4.25,4.25,0,0,1,.08-0.92,0.87,0.87,0,0,1,.59-0.59,10.79,10.79,0,0,1,2.09-.61,16,16,0,0,1,3.34-.31,14.34,14.34,0,0,1,3.37.33A4.7,4.7,0,0,1,215.24,22a4.18,4.18,0,0,1,1.13,1.92,10.48,10.48,0,0,1,.33,2.84v16a4.15,4.15,0,0,0,0,.44,2.87,2.87,0,0,1,0,.36,1.33,1.33,0,0,1-.36,1.19,3.89,3.89,0,0,1-1.53.19H212l-0.65,0a0.41,0.41,0,0,1-.33-0.19,1.17,1.17,0,0,1-.15-0.46,6.87,6.87,0,0,1,0-.88h-0.29Zm-2.8-5.64q0,2.09,1.5,2.09a1.25,1.25,0,0,0,1.21-.54,4.16,4.16,0,0,0,.29-1.84V34.95a3.41,3.41,0,0,0-.29-1.61,1.14,1.14,0,0,0-1.09-.52q-1.63,0-1.63,2.26v2.68Z" style="fill: #ed1d24"></path>
+ <path d="M230,20.27a1.8,1.8,0,0,1-.4,0,1.67,1.67,0,0,0-.36,0q-0.83,0-.84,1.13c0,0.14,0,.29,0,0.44s0,0.33.06,0.52h1.59q1,0,1,1.34v3.05a1.42,1.42,0,0,1-.36,1.13,2.15,2.15,0,0,1-1.32.29h-0.59V43.18a2.93,2.93,0,0,1-.25,1.44,1.63,1.63,0,0,1-1.38.4,7.64,7.64,0,0,1-.84,0,5.61,5.61,0,0,0-.58,0,4.8,4.8,0,0,0-.8.06,4.82,4.82,0,0,1-.79.06,1.05,1.05,0,0,1-1.13-.48,5.45,5.45,0,0,1-.17-1.61V28.17a2.9,2.9,0,0,1-.54,0,0.81,0.81,0,0,1-.86-0.38,4.16,4.16,0,0,1-.15-1.34V23.62a1.87,1.87,0,0,1,.23-1.07,1,1,0,0,1,.86-0.31l0.21,0,0.25,0v-3.8q0-4.05,4.22-4.06h1.71a5.08,5.08,0,0,1,1.8.21,1.44,1.44,0,0,1,.46,1.38v3A1,1,0,0,1,230,20.27Z" style="fill: #ed1d24"></path>
+ <path d="M235.22,32.81q0-3.39.21-5.66a7.26,7.26,0,0,1,1.17-3.62,6.2,6.2,0,0,1,2.61-2.32,9,9,0,0,1,3.62-.65,8.35,8.35,0,0,1,3.55.67,5.18,5.18,0,0,1,2.26,2,9.9,9.9,0,0,1,1.19,3.47,29.9,29.9,0,0,1,.36,4.93q0,3.6-.12,6a9.69,9.69,0,0,1-.79,3.74,6.6,6.6,0,0,1-2.63,3,8.24,8.24,0,0,1-4.1.92,7.61,7.61,0,0,1-3.62-.75,5.48,5.48,0,0,1-2.24-2.3,11.43,11.43,0,0,1-1.15-3.91A42.7,42.7,0,0,1,235.22,32.81Zm6,4.1q0,1.92,1.38,1.92a1.41,1.41,0,0,0,1.23-.5,2.89,2.89,0,0,0,.36-1.63V29.76a5.79,5.79,0,0,0-.27-2.07,1.31,1.31,0,0,0-1.36-.65q-1.34,0-1.34,2v7.86Z" style="fill: #ed1d24"></path>
+ <path d="M265.72,44.83a1.48,1.48,0,0,1-.77-0.36,1.31,1.31,0,0,1-.33-0.71,6.64,6.64,0,0,1-.08-1.17v-11a19.57,19.57,0,0,0-.21-3.3A1.21,1.21,0,0,0,263,27.21q-1.8,0-1.8,3.09v9a17.45,17.45,0,0,0,.11,1.94,16.78,16.78,0,0,1,.1,1.82,3.22,3.22,0,0,1-.12,1,1.12,1.12,0,0,1-.38.54,1.27,1.27,0,0,1-.65.23,9.61,9.61,0,0,1-1,0h-2.8a1.39,1.39,0,0,1-1.26-.46,3.72,3.72,0,0,1-.29-1.8V25.83q0-.38,0-1t0-1.46a7.91,7.91,0,0,1,.08-1.28,1.37,1.37,0,0,1,.36-0.75A1.52,1.52,0,0,1,256.1,21a7.48,7.48,0,0,1,1.4-.1h2.38a1.88,1.88,0,0,1,1.21.27,1.69,1.69,0,0,1,.29,1.19v0.29l0.21,0a4.92,4.92,0,0,1,4.1-2.09,4.13,4.13,0,0,1,3.91,2,12.78,12.78,0,0,1,1.15,6.12V41.59a7.51,7.51,0,0,1-.31,2.55,1.65,1.65,0,0,1-1.73.79h-1.59A7.44,7.44,0,0,1,265.72,44.83Z" style="fill: #ed1d24"></path>
+ <path d="M290.07,42.64a1.59,1.59,0,0,1-.42,1.34,7.53,7.53,0,0,1-2,.86,11.18,11.18,0,0,1-3.22.44q-4.93,0-7.06-3.05t-2.13-9.53q0-6.19,1.82-9.13t6-2.95a6.86,6.86,0,0,1,6.35,3.64,7.9,7.9,0,0,1,.86,3.22q0.15,1.92.15,4.56a9.4,9.4,0,0,1-.25,2.51,1.19,1.19,0,0,1-1.3.84h-6.73a0.75,0.75,0,0,0-.44.17,0.47,0.47,0,0,0-.23.38,2.89,2.89,0,0,0,.86,2.32,4.34,4.34,0,0,0,2.82.73,3.9,3.9,0,0,0,1.19-.17,6.58,6.58,0,0,0,.94-0.38L288,38a1.35,1.35,0,0,1,.59-0.17,1.33,1.33,0,0,1,1.46,1.55v3.22Zm-6.48-12.17a1,1,0,0,0,.88-0.29,2.32,2.32,0,0,0,.17-1,5.5,5.5,0,0,0-.36-2.28,1.44,1.44,0,0,0-1.44-.73q-1.42,0-1.42,2.34a5.69,5.69,0,0,0,.15,1.55,0.67,0.67,0,0,0,.73.46h1.29Z" style="fill: #ed1d24"></path>
+ </g>
+ <g>
+ <rect x="9.7" y="74.68" width="415.88" height="0.33" style="fill: #fff"></rect>
+ <rect x="9.7" y="111" width="415.88" height="0.33" style="fill: #fff"></rect>
+ <rect x="9.7" y="150.75" width="415.88" height="0.33" style="fill: #fff"></rect>
+ <rect x="9.7" y="191.44" width="415.88" height="0.33" style="fill: #fff"></rect>
+ <rect x="9.7" y="227.55" width="415.88" height="0.33" style="fill: #fff"></rect>
+ </g>
+ <g class="letters">
+ <path class="entryletter" d="M55.86,84.95q0-1.07-.05-2.31t-0.05-2.73q0-2.9,1-4t3.7-1.13q0.75,0,1.45.05c0.46,0,.95.05,1.45,0.05s1,0,1.45-.05,1-.05,1.55-0.05H81.49c0.79,0,1.46,0,2-.05s1-.05,1.39-0.05q2.79,0,3.86,1.13t1.07,4.45q0,1.18-.11,2.41t-0.11,2.63A16.47,16.47,0,0,1,89.32,89a2.81,2.81,0,0,1-1.39,1.88,7,7,0,0,1-2.95.7q-1.88.11-5,.11H77a11,11,0,0,0-2.09.16,1.67,1.67,0,0,0-1.18.8,5,5,0,0,0-.54,1.82A25.06,25.06,0,0,0,73,97.71q0,2,.11,3.11a2.55,2.55,0,0,0,.64,1.61,2.78,2.78,0,0,0,1.77.64q1.23,0.11,3.48.11h0.75a24.62,24.62,0,0,0,3.38-.21,21,21,0,0,1,2.84-.21,2.59,2.59,0,0,1,2.47,1.13,7.57,7.57,0,0,1,.64,3.59q0,1.07-.05,2.09c0,0.68-.05,1.38-0.05,2.09v2.68A8.6,8.6,0,0,1,88,119q-1,1.39-4.18,1.39-1.29,0-2.89-.11t-3.43-.11q-2.68,0-3.48,1t-0.8,4.45v15.55q0,2.57.16,4.34t0.16,2.63a4.26,4.26,0,0,1-1,3.11,4.44,4.44,0,0,1-3.22,1,23.59,23.59,0,0,1-3.11-.21,26.06,26.06,0,0,0-3.43-.21,15.12,15.12,0,0,0-2.25.16,15.15,15.15,0,0,1-2.25.16q-1.83,0-2.2-.91a8.76,8.76,0,0,1-.38-3.06v-2.14c0-.57,0-1.14.05-1.72s0.05-1.14.05-1.72V84.95Z" style="fill: #fff" data-svg-origin="273.68751525878906 199.67999267578125" transform="matrix(1,0,0,1,0,0)"></path>
+
+ <path class="entryletter" d="M94.77,134.54Q94,133.41,94,130V86.66q0-1-.11-2.52t-0.11-3.7A19.83,19.83,0,0,1,94,77.17a3.5,3.5,0,0,1,.91-1.93,3.84,3.84,0,0,1,2-1,18.3,18.3,0,0,1,3.54-.27h5.25a5.78,5.78,0,0,1,2,.27,1.81,1.81,0,0,1,1,1,5.27,5.27,0,0,1,.38,1.93q0.05,1.23.05,3.16l0.75,0.11a17.49,17.49,0,0,1,2.73-5.68q1.55-1.93,2.95-1.93a2.61,2.61,0,0,1,2.68,1.5,12.38,12.38,0,0,1,.64,4.61V90.42a20.33,20.33,0,0,1-.11,2.36,2.35,2.35,0,0,1-.43,1.23,1.76,1.76,0,0,1-.86.54,8.85,8.85,0,0,1-1.5.27,7,7,0,0,0-4.34,2q-1.23,1.5-1.23,4.93V121.4q0,2.68.21,4.93t0.21,4.07q0,3.54-.8,4.4t-3.49.86H98Q95.52,135.67,94.77,134.54Z" style="fill: #fff" data-svg-origin="311.77760314941406 197.81153106689453" transform="matrix(1,0,0,1,0,0)"></path>
+
+ <path class="entryletter" d="M158.57,129.77q0,2.68-1.07,3.43a19.2,19.2,0,0,1-5.25,2.2,28.64,28.64,0,0,1-8.26,1.13q-12.65,0-18.12-7.83t-5.47-24.45q0-15.87,4.66-23.43t15.49-7.56q10.94,0,16.3,9.33a20.29,20.29,0,0,1,2.2,8.26q0.38,4.93.38,11.69a23.93,23.93,0,0,1-.64,6.43q-0.64,2.15-3.32,2.14H138.19a1.94,1.94,0,0,0-1.13.43,1.21,1.21,0,0,0-.59,1q0,4.08,2.2,6t7.24,1.88a10,10,0,0,0,3.06-.43,16.78,16.78,0,0,0,2.41-1l1.93-1a3.48,3.48,0,0,1,1.5-.43q3.75,0,3.75,4v8.26Zm-16.62-31.2q1.82,0,2.25-.75a5.89,5.89,0,0,0,.43-2.68,14.11,14.11,0,0,0-.91-5.84q-0.91-1.87-3.7-1.88-3.65,0-3.65,6a14.57,14.57,0,0,0,.38,4,1.73,1.73,0,0,0,1.88,1.18h3.32Z" style="fill: #fff" data-svg-origin="338.39998626708984 198.2599868774414" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M201.67,129.77q0,2.68-1.07,3.43a19.19,19.19,0,0,1-5.25,2.2,28.64,28.64,0,0,1-8.26,1.13q-12.66,0-18.12-7.83t-5.47-24.45q0-15.87,4.67-23.43t15.49-7.56q10.94,0,16.3,9.33a20.28,20.28,0,0,1,2.2,8.26q0.38,4.93.38,11.69a23.94,23.94,0,0,1-.64,6.43q-0.64,2.15-3.32,2.14H181.3a1.94,1.94,0,0,0-1.13.43,1.21,1.21,0,0,0-.59,1q0,4.08,2.2,6t7.24,1.88a10,10,0,0,0,3.06-.43,16.78,16.78,0,0,0,2.41-1l1.93-1a3.48,3.48,0,0,1,1.5-.43q3.75,0,3.75,4v8.26ZM185,98.56q1.82,0,2.25-.75a5.89,5.89,0,0,0,.43-2.68,14.09,14.09,0,0,0-.91-5.84q-0.91-1.87-3.7-1.88-3.64,0-3.64,6a14.53,14.53,0,0,0,.38,4,1.73,1.73,0,0,0,1.88,1.18H185Z" style="fill: #fff" data-svg-origin="381.5 198.26000213623047" transform="matrix(1,0,0,1,0,0)"></path>
+
+
+ <path class="entryletter" d="M206.05,139.1a15.65,15.65,0,0,1,1.45-6.22q1.45-3.32,3.27-7.93,2.79-7.08,5.2-12.49t4.24-9.33Q222,99.21,223,96.8a10.51,10.51,0,0,0,1-3.49q0-.86-1.29-1.07a29.74,29.74,0,0,0-4.29-.21q-3.11,0-4.88.11t-2.3.11q-2.68,0-3.54-1.39a8.93,8.93,0,0,1-.86-4.5q0-.86.05-1.39c0-.36.05-0.61,0.05-0.75,0-.36,0-1-0.05-1.82s-0.05-2-.05-3.32a3.53,3.53,0,0,1,1.34-3.22A8.62,8.62,0,0,1,212.7,75h28.52a3.93,3.93,0,0,1,2.68.64,3.16,3.16,0,0,1,.91,2.14,39.47,39.47,0,0,1,.16,4v3.65a11,11,0,0,1-.27,1.39,23.11,23.11,0,0,1-.91,2.89L227,130.84c-0.21.57-.41,1.06-0.59,1.45a2.71,2.71,0,0,0-.27,1.13q0,0.86,5.68.86h2.89a43,43,0,0,0,4.77-.21,27.13,27.13,0,0,1,2.74-.21,2.64,2.64,0,0,1,2.14,1.07,4.34,4.34,0,0,1,.86,2.79q0,1.82-.11,3.38T245,143.92c0,0.36,0,.79.05,1.29s0.05,1.11.05,1.82q0,3-.91,4a3.93,3.93,0,0,1-3.05,1q-4.4,0-8.1-.16t-6.81-.16q-2.47,0-5.15.16t-5.68.16H213q-1.72,0-2.73.05l-1.23.05a2.78,2.78,0,0,1-2.41-1,5.87,5.87,0,0,1-.7-3.32Z" style="fill: #fff" data-svg-origin="423.9086151123047 199.96994018554688" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M287.51,76.37a2.11,2.11,0,0,0-1.91-1.71,9.51,9.51,0,0,0-1.32-.1h-9.54a1.42,1.42,0,0,0-.81.1,1.79,1.79,0,0,1-1.48.26,28.56,28.56,0,0,0-8.25.4,14.58,14.58,0,0,0-8.11,4.38s-4.92,4.44-5.47,13c0,0.54-.06,1.09-0.05,1.64h0v0c0,0.17,0,.33,0,0.5q0,0.87,0,1.74c0,6.36,0,12.72,0,19.08,0,3.73-.09,7.47-0.09,11.2q0,18.34.09,36.67a6.69,6.69,0,0,1-.07.67l-0.76-.3a29.09,29.09,0,0,0-24.62.83,30.5,30.5,0,0,0-13.6,14.39A37.18,37.18,0,0,0,208.34,200a33.17,33.17,0,0,0,5.37,14.83c6.69,9.85,16,14.44,27.89,13.24a27.22,27.22,0,0,0,17.29-8.65,34.17,34.17,0,0,0,9.4-22.2c0.11-1.65,0-3.31,0-5q0-27,0-54c0-.27,0-0.54,0-0.85l0.78,0c1.46-.11,2.93-0.14,4.37-0.35a15.33,15.33,0,0,0,8.71-4.09,18.07,18.07,0,0,0,3.84-5.44,21.16,21.16,0,0,0,1.61-7.83V77.27C287.63,77,287.58,76.67,287.51,76.37Zm-14.28,39.34a12.15,12.15,0,0,1-.33,2.71,3.05,3.05,0,0,1-3.07,2.53,7.46,7.46,0,0,1-.91,0,2.88,2.88,0,0,1-2.64-1.82,6.64,6.64,0,0,1-.5-2.75q0-3.36,0-6.72v-3.57c0-3.32,0-6.63,0-10a10.24,10.24,0,0,1,.35-2.61,2.88,2.88,0,0,1,3.18-2.32,6.59,6.59,0,0,1,.91.06,2.71,2.71,0,0,1,2.55,2.33,27.48,27.48,0,0,1,.44,4C273.28,103.65,273.26,109.68,273.24,115.71Z" style="fill: #ed1d24;fill-rule: evenodd" data-svg-origin="425.99229431152344 199.53984832763672" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M323.18,135.4a3.8,3.8,0,0,1-2-.91,3.34,3.34,0,0,1-.86-1.82,17.11,17.11,0,0,1-.21-3V101.57a50,50,0,0,0-.54-8.47,3.11,3.11,0,0,0-3.32-2.89q-4.61,0-4.61,7.93V121.3a44.44,44.44,0,0,0,.27,5,42.48,42.48,0,0,1,.27,4.66,8.33,8.33,0,0,1-.32,2.63,2.89,2.89,0,0,1-1,1.39,3.29,3.29,0,0,1-1.66.59,24.5,24.5,0,0,1-2.52.11h-7.18q-2.47,0-3.22-1.18t-0.75-4.61V86.66q0-1-.11-2.57t-0.11-3.75a19.73,19.73,0,0,1,.21-3.27,3.49,3.49,0,0,1,.91-1.93,3.88,3.88,0,0,1,2-1,19.17,19.17,0,0,1,3.59-.27h6.11a4.82,4.82,0,0,1,3.11.7q0.75,0.7.75,3.06v0.75l0.54,0.11a12.63,12.63,0,0,1,10.51-5.36q7.08,0,10,5.2t2.95,15.71v33a19.31,19.31,0,0,1-.8,6.54q-0.81,2-4.45,2h-4.07A19,19,0,0,1,323.18,135.4Z" style="fill: #fff" data-svg-origin="513.3064270019531 198.1286849975586" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M378.67,129.77q0,2.68-1.07,3.43a19.19,19.19,0,0,1-5.25,2.2,28.63,28.63,0,0,1-8.26,1.13q-12.65,0-18.12-7.83t-5.47-24.45q0-15.87,4.66-23.43t15.49-7.56q10.94,0,16.3,9.33a20.31,20.31,0,0,1,2.2,8.26q0.37,4.93.38,11.69a23.92,23.92,0,0,1-.64,6.43q-0.64,2.15-3.32,2.14H358.29a1.94,1.94,0,0,0-1.12.43,1.2,1.2,0,0,0-.59,1q0,4.08,2.2,6t7.24,1.88a10,10,0,0,0,3.06-.43,16.78,16.78,0,0,0,2.41-1l1.93-1a3.48,3.48,0,0,1,1.5-.43q3.75,0,3.75,4v8.26ZM362,98.56q1.82,0,2.25-.75a5.92,5.92,0,0,0,.43-2.68,14.13,14.13,0,0,0-.91-5.84q-0.91-1.87-3.7-1.88-3.65,0-3.65,6a14.61,14.61,0,0,0,.38,4,1.73,1.73,0,0,0,1.88,1.18H362Z" style="fill: #fff" data-svg-origin="558.5 198.26000213623047" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M77.43,211.93v-0.45a6.83,6.83,0,0,1,1.12-4.38q1.12-1.35,3.93-1.35a3.5,3.5,0,0,1,1.63.45q0.84,0.45,2,1a21.12,21.12,0,0,0,2.64,1,13.48,13.48,0,0,0,3.77.45q5.73,0,5.73-4.95a6.91,6.91,0,0,0-.9-3.48A14,14,0,0,0,94.85,197q-1.57-1.52-3.6-3.2T87,190a31.14,31.14,0,0,1-7.25-9.83,28.44,28.44,0,0,1-2.19-11.52q0-22.81,25.29-22.81,4.16,0,6.52.22a8,8,0,0,1,3.48,1,3,3,0,0,1,1.35,2.19,26.17,26.17,0,0,1,.23,3.82v8.2q0,3.37-.67,4.44t-3.26,1.07a11.65,11.65,0,0,1-4.1-.73,11.71,11.71,0,0,0-4.1-.73q-5.17,0-5.17,4.5a5.73,5.73,0,0,0,1,3.09,18.1,18.1,0,0,0,2.75,3.26q1.8,1.74,4.21,3.93t5.34,5.11a26,26,0,0,1,6.07,8.71,27.59,27.59,0,0,1,1.69,10.06q0,12.48-5.73,18.43t-18.21,6A48.83,48.83,0,0,1,85,227.6a22.64,22.64,0,0,1-5.28-1.52,3.14,3.14,0,0,1-1.85-1.91,12.39,12.39,0,0,1-.28-2.92v-0.79Z" style="fill: #fff" data-svg-origin="295.4041748046875 270.8400115966797" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M126.77,181.92a11.56,11.56,0,0,1-2.87-.28,2.89,2.89,0,0,1-1.63-1,4.15,4.15,0,0,1-.73-2,25.21,25.21,0,0,1-.17-3.2q0-2.7.11-4.55a7.27,7.27,0,0,1,.67-2.92,3.41,3.41,0,0,1,1.74-1.57,8.28,8.28,0,0,1,3.2-.51h0.34v-9.44q0-2.13.11-3.48a3.88,3.88,0,0,1,.67-2.08,2.67,2.67,0,0,1,1.63-1,15.35,15.35,0,0,1,3-.22h1.8q0.9,0,2.47-.06t3.82-.06q2.58,0,3.15,1.35a11.92,11.92,0,0,1,.56,4.27v10.56h0.79q3.15,0,4.16,1t1,3.77v6.63a16,16,0,0,1-.17,2.64,2.47,2.47,0,0,1-.73,1.46,3.35,3.35,0,0,1-1.8.67,25,25,0,0,1-3.26.17v24.39q0,2.58,2,2.58,2.25,0,2.87.62t0.62,3.77v8.77q0,2.92-1.07,3.93a5.26,5.26,0,0,1-3.65,1h-4.5a53.33,53.33,0,0,1-6.58-.34,7.37,7.37,0,0,1-4.21-1.8,8.61,8.61,0,0,1-2.3-5.28,77.25,77.25,0,0,1-.39-8.65V181.92h-0.67Z" style="fill: #fff" data-svg-origin="339.3685989379883 274.55003356933594" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M166.78,162.47a24,24,0,0,1,3.71.22,4,4,0,0,1,2.08.9,3.33,3.33,0,0,1,1,1.91,18,18,0,0,1,.23,3.15v29.56a52.39,52.39,0,0,0,.56,8.88,3.26,3.26,0,0,0,3.48,3q4.83,0,4.83-8.32V177.54a46.22,46.22,0,0,0-.28-5.11,45.22,45.22,0,0,1-.28-5q0-3.48,1.29-4.21a9.69,9.69,0,0,1,4.44-.73h7.53q2.58,0,3.37,1.18t0.79,4.78v45.4c0,0.75,0,1.67.11,2.75s0.11,2.38.11,3.88a21,21,0,0,1-.22,3.43,3.68,3.68,0,0,1-1,2,4.06,4.06,0,0,1-2.14,1,20,20,0,0,1-3.76.28h-6.41q-2.47,0-3.26-.73t-0.79-3.2v-0.79l-0.56-.11a13.13,13.13,0,0,1-11,5.51q-7.42,0-10.51-5.45T157,206.08V171.47q0-2.36.11-4a7.32,7.32,0,0,1,.67-2.81,3.4,3.4,0,0,1,1.69-1.63,7.53,7.53,0,0,1,3-.51h4.27Zm9-6.63a1.59,1.59,0,0,1-.67,1.57,5.41,5.41,0,0,1-2.25.34H166a4.09,4.09,0,0,1-2-.39,1.86,1.86,0,0,1-.67-1.74v-7A1.73,1.73,0,0,1,164,147a4.72,4.72,0,0,1,2.25-.39h6.85a3.66,3.66,0,0,1,2.08.45,2.13,2.13,0,0,1,.62,1.8v7Zm18.54,0a1.59,1.59,0,0,1-.67,1.57,5.42,5.42,0,0,1-2.25.34h-7a3.76,3.76,0,0,1-2-.39,1.94,1.94,0,0,1-.62-1.74v-7a1.73,1.73,0,0,1,.67-1.63,4.72,4.72,0,0,1,2.25-.39h6.86a3.65,3.65,0,0,1,2.08.45,2.12,2.12,0,0,1,.62,1.8v7Z" style="fill: #fff" data-svg-origin="375 271.5874786376953" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M279.84,219.31a3.17,3.17,0,0,1,1.93.51,2.15,2.15,0,0,0,1.25.51q2.27,0,2.27-1.71a15.28,15.28,0,0,0-.34-3l-9.43-47.39q-0.23-.91-0.34-1.59a6.88,6.88,0,0,1-.11-0.91,2.93,2.93,0,0,1,1.08-2.44,4.54,4.54,0,0,1,2.9-.85h9.77q2.16,0,2.61,1a12.81,12.81,0,0,1,.68,3.64l3.75,40.57h2l3.18-42a3.75,3.75,0,0,1,1-2.56,5,5,0,0,1,3-.62h8.29q4.54,0,4.55,3.52a12.88,12.88,0,0,1-.46,3l-11.36,53.52q-1.82,8.29-5.63,11.93t-11.31,3.64a28.37,28.37,0,0,1-9.09-1.08q-3-1.08-3-3.69v-9.77Q277.11,219.31,279.84,219.31Z" style="fill: #fff" data-svg-origin="493.0654296875 287.43333435058594" transform="matrix(1,0,0,1,0,0)"></path>
+ <path class="entryletter" d="M322,194.76q0-9.21.57-15.4a19.72,19.72,0,0,1,3.18-9.83,16.85,16.85,0,0,1,7.1-6.31,24.52,24.52,0,0,1,9.83-1.76,22.65,22.65,0,0,1,9.66,1.82,14,14,0,0,1,6.13,5.57,26.76,26.76,0,0,1,3.24,9.43,81,81,0,0,1,1,13.41q0,9.78-.34,16.31t-2.16,10.17a17.93,17.93,0,0,1-7.16,8.07,22.44,22.44,0,0,1-11.14,2.5,20.7,20.7,0,0,1-9.83-2,14.91,14.91,0,0,1-6.08-6.25,30.9,30.9,0,0,1-3.12-10.62A116.33,116.33,0,0,1,322,194.76Zm16.36,11.14q0,5.23,3.75,5.23a3.82,3.82,0,0,0,3.35-1.36,7.84,7.84,0,0,0,1-4.43V186.46a15.66,15.66,0,0,0-.74-5.62q-0.74-1.76-3.69-1.76-3.64,0-3.64,5.45V205.9Z" style="fill: #fff" data-svg-origin="539.9972229003906 286.44805908203125" transform="matrix(1,0,0,1,0,0)"></path>
+ </g>
+ </svg>
+ </div>
+ </section>
+</body></html>
diff --git a/gfx/tests/crashtests/1470437.html b/gfx/tests/crashtests/1470437.html
new file mode 100644
index 0000000000..c88008fe00
--- /dev/null
+++ b/gfx/tests/crashtests/1470437.html
@@ -0,0 +1,10 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
+:last-child{
+ background-image: -moz-element(#id3);
+ border-width: 9596.6vmin;
+ border-style: dotted;
+}
+</style>
+</head><body><rect id="id3">
+</rect></body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1470440.html b/gfx/tests/crashtests/1470440.html
new file mode 100644
index 0000000000..646070436c
--- /dev/null
+++ b/gfx/tests/crashtests/1470440.html
@@ -0,0 +1,7 @@
+<style>
+:last-child{
+ border-width: 9596.6vmin thin;
+ border-style: dotted;
+}
+</style>
+<rect id='id3'/>
diff --git a/gfx/tests/crashtests/1478035.html b/gfx/tests/crashtests/1478035.html
new file mode 100644
index 0000000000..39255e5d28
--- /dev/null
+++ b/gfx/tests/crashtests/1478035.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html class="reftest-wait"><body>
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 966 520">
+ <defs>
+ <filter id="goo">
+ <feGaussianBlur in="SourceGraphic" stdDeviation="11" result="blur"></feGaussianBlur>
+ <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 21 -9"></feColorMatrix>
+ <feBlend></feBlend>
+ </filter>
+ <path id="bean" d="M468,146.24c8,4.81,3.39,13.19,10.53,14.61s21.48-.6,21.77,9.12c.23,8-18.6,18.21-38,9.64-12-5.29-16.84-17.93-15.69-25.65C448.27,142.45,460.86,142,468,146.24Z"></path>
+ </defs>
+ <g filter="url(#goo)" clip-path="url(#worldMapMask)" opacity="0.1" fill="#3ac0f0" style="opacity: 0.054;">
+ <use id="animated" xlink:href="#bean" style="transform-origin: 0px 0px 0px;" transform="matrix(0.40499,0,0,0.40499,281.6573805236819,97.17512744903576)"></use>
+ </g>
+</svg>
+
+<script>
+var animated = document.getElementById('animated');
+var transforms = [
+ 'matrix(0.40499,0,0,0.40499,281.65738000000005,97.17513000000001)',
+ 'matrix(0,0,0,0,473.36901451812747,163.31791128669738)',
+ 'matrix(0.40499,0,0,0.40499,281.65738000000005,97.17513000000001)',
+ 'matrix(0,0,0,0,473.36901451812747,163.31791128669738)',
+ 'matrix(9,0,0,9,-3786.9947244955442,-1306.5579660936737)',
+ 'matrix(0,0,0,0,473.36901451812747,163.31791128669738)',
+ 'matrix(3,0,0,3,-946.7522318197632,-326.64071450675965)'
+];
+var index = 0;
+function nextFrame() {
+ animated.setAttribute('transform', transforms[index]);
+ index++;
+ if (index < transforms.length) {
+ requestAnimationFrame(nextFrame);
+ } else {
+ document.documentElement.classList.remove('reftest-wait');
+ }
+}
+window.addEventListener('load', nextFrame);
+</script>
+</body></html>
diff --git a/gfx/tests/crashtests/1490704-1.html b/gfx/tests/crashtests/1490704-1.html
new file mode 100644
index 0000000000..9352d80e5f
--- /dev/null
+++ b/gfx/tests/crashtests/1490704-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <style class="">
+ * {
+ transform: scalex(10) !important;
+ block-size: calc(-25958*-8px + -23%);
+ }
+
+ @page :left {
+ @page :left {
+ *:link {}
+ }
+ }
+
+ * {
+ box-shadow: green calc(42em - 43357px) 45px, blue -3841px 125px, currentColor 21px 50268px, 168px 2px 50817px 253px orange;
+ columns: 2 auto ! important;
+ }
+ </style>
+</head>
+
+<body class="" style="inline-size:-moz-min-content!important">
+ <p class=""><canvas class="" width="1024">
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1494062-blob-image-wraplist-clip.html b/gfx/tests/crashtests/1494062-blob-image-wraplist-clip.html
new file mode 100644
index 0000000000..670cb2551e
--- /dev/null
+++ b/gfx/tests/crashtests/1494062-blob-image-wraplist-clip.html
@@ -0,0 +1,27 @@
+<html class="reftest-wait">
+<head>
+<script type="text/javascript">
+window.addEventListener("MozReftestInvalidate", function() {
+ window.requestAnimationFrame(function() {
+ var xmlns = "http://www.w3.org/2000/svg";
+ var circleElm = document.getElementById("green_circle");
+ circleElm.setAttribute("clip-path", "url(#quarter)");
+ window.requestAnimationFrame(function() {
+ document.documentElement.removeAttribute('class');
+ });
+ });
+});
+</script>
+</head>
+<body>
+<svg width="200" height="200" id="root">
+<defs>
+<clipPath id="quarter">
+<rect x="0" y="0" width="50" height="50"/>
+</clipPath>
+</defs>
+<circle cx="50" cy="50" r="40" fill="green" id="green_circle"/>
+<rect x="150" y="150" width="10" height="10" fill="red"/>
+</svg>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/1496194.html b/gfx/tests/crashtests/1496194.html
new file mode 100644
index 0000000000..b0bf1b8472
--- /dev/null
+++ b/gfx/tests/crashtests/1496194.html
@@ -0,0 +1,14 @@
+<style>
+/*
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+ */
+* {
+ position: sticky;
+ padding-right: 1px;
+ left: 2px;
+ right: 163px;
+ display: ruby-base;
+}
+</style>
diff --git a/gfx/tests/crashtests/1501518.html b/gfx/tests/crashtests/1501518.html
new file mode 100644
index 0000000000..a03e92697a
--- /dev/null
+++ b/gfx/tests/crashtests/1501518.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- Add a few levels of blur to put as at an earlier render pass than the rest of the browser UI.
+ We then trigger a render pass with a larger border and a small rectangle. The border gets drawn
+ in this pass because it goes into the texture cache, and the rectangle gets drawn in this pass
+ because it gets an additional blur. A large item drawn to the texture cache in a pass where
+ the other objects are small will trigger the allocation of a render target that's smaller than
+ the large item, which is fine but triggered a panic before this bug was fixed.
+-->
+<div style="filter: blur(1px);">
+ <div style="filter: blur(1px);">
+ <div style="filter: blur(1px);">
+ <div style="background: green; height: 50px; width: 1000px; border: 10px dotted black;"></div>
+ <div style="background: blue; height: 50px; width: 50px; filter: blur(1px);"</div>
+ </div>
+ </div>
+</div>
diff --git a/gfx/tests/crashtests/1503986-1.html b/gfx/tests/crashtests/1503986-1.html
new file mode 100644
index 0000000000..016ac4084a
--- /dev/null
+++ b/gfx/tests/crashtests/1503986-1.html
@@ -0,0 +1,8 @@
+<canvas id='c'></canvas>
+<script>
+document.addEventListener("DOMContentLoaded", function(){
+var a=document.getElementById('c').getContext('2d');
+ a.setLineDash([17]);
+ a.strokeText('L', 53, 1);
+});
+</script>
diff --git a/gfx/tests/crashtests/1505426-1.html b/gfx/tests/crashtests/1505426-1.html
new file mode 100644
index 0000000000..d6eef7c99d
--- /dev/null
+++ b/gfx/tests/crashtests/1505426-1.html
@@ -0,0 +1,23 @@
+<html>
+
+<head>
+ <script>
+ function start() {
+ canvas = document.getElementById('canvas')
+ context = canvas.getContext('2d')
+ canvas.setAttribute('x', 800)
+ setTimeout(function() {
+ context.fillText('i', 158, 156, 206)
+ }, 0)
+ context.setTransform(0, 1, 19, 1, 0.0989504886744, 0)
+ }
+ document.addEventListener('DOMContentLoaded', start)
+ </script>
+
+</head>
+
+<body>
+ <canvas id='canvas'></canvas>
+</body>
+
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1505934-1.html b/gfx/tests/crashtests/1505934-1.html
new file mode 100644
index 0000000000..67475fecf9
--- /dev/null
+++ b/gfx/tests/crashtests/1505934-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<style>
+ body {
+ background-color: limegreen;
+ }
+ .A {
+ transform: scale(0.01) perspective(1000px);
+ transform-origin: 0% 0%;
+ filter: grayscale(40%);
+ }
+ .B {
+ background-color: black;
+ width: 10000px;
+ height: 5000px;
+ border-radius: 2000px;
+ }
+</style>
+<body>
+ <div class="A">
+ <div class = "B"/>
+ </div>
+</body>
diff --git a/gfx/tests/crashtests/1508811.html b/gfx/tests/crashtests/1508811.html
new file mode 100644
index 0000000000..2e762a98a0
--- /dev/null
+++ b/gfx/tests/crashtests/1508811.html
@@ -0,0 +1,10 @@
+<style>
+* {
+ -webkit-background-clip: text;
+ column-count: 7;
+}
+</style>
+<dialog open="true">
+<dl>
+<dd>
+<menu>
diff --git a/gfx/tests/crashtests/1508822.html b/gfx/tests/crashtests/1508822.html
new file mode 100644
index 0000000000..a3b6ce8e1e
--- /dev/null
+++ b/gfx/tests/crashtests/1508822.html
@@ -0,0 +1,5 @@
+<style>
+* { scale: 0.55749 10 1 }
+</style>
+<svg>
+<circle r="49%" mask="url()">
diff --git a/gfx/tests/crashtests/1509099.html b/gfx/tests/crashtests/1509099.html
new file mode 100644
index 0000000000..861299e595
--- /dev/null
+++ b/gfx/tests/crashtests/1509099.html
@@ -0,0 +1,7 @@
+<style>
+* { transform: scale(-1, 5) }
+</style>
+<marquee height="1">A</marquee>
+<br/><br/>
+<svg>
+<ellipse rx="5" ry="93%" stroke-width="0.6" stroke="red">
diff --git a/gfx/tests/crashtests/1509123.html b/gfx/tests/crashtests/1509123.html
new file mode 100644
index 0000000000..375ce0c553
--- /dev/null
+++ b/gfx/tests/crashtests/1509123.html
@@ -0,0 +1,12 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
+* {
+ -webkit-text-stroke-width: 90px;
+ -webkit-transform: scale(9);
+}
+</style>
+</head><body><ol>
+<li>
+<dialog open="true">
+<audio controls="controls">
+</audio></dialog></li></ol></body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1513133.html b/gfx/tests/crashtests/1513133.html
new file mode 100644
index 0000000000..2f4e668691
--- /dev/null
+++ b/gfx/tests/crashtests/1513133.html
@@ -0,0 +1,11 @@
+<style>
+#a { scale: 47 9 }
+#b {
+ font: 31em Ahem, serif;
+ border-top-left-radius: 2vmin;
+ mix-blend-mode: color-burn;
+ -webkit-background-clip: text;
+}
+</style>
+<pre id="a">
+<textarea id="b">
diff --git a/gfx/tests/crashtests/1524418.html b/gfx/tests/crashtests/1524418.html
new file mode 100644
index 0000000000..a6191a9054
--- /dev/null
+++ b/gfx/tests/crashtests/1524418.html
@@ -0,0 +1,11 @@
+<script>
+window.onload=function() {
+ a.setAttribute('style', 'font-size:3154')
+}
+</script>
+<style>
+* {
+ background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciLz4=);
+}
+</style>
+<select size='63' id='a'>
diff --git a/gfx/tests/crashtests/1529149.html b/gfx/tests/crashtests/1529149.html
new file mode 100644
index 0000000000..6f6b82dab8
--- /dev/null
+++ b/gfx/tests/crashtests/1529149.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+<style>
+:not(option) {
+ position: absolute;
+}
+#a {
+ background: url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=) fixed;
+}
+fieldset:first-child {
+ column-width: 0;
+ filter: invert(3);
+}
+</style>
+<script>
+function go() {
+ a.multiple = true
+ document.documentElement.classList.remove("reftest-wait");
+}
+</script>
+<body onload=go()>
+<fieldset style="scale: 1 9 0.3472">
+<select id="a">
+</html>
diff --git a/gfx/tests/crashtests/1535657.html b/gfx/tests/crashtests/1535657.html
new file mode 100644
index 0000000000..b35072c3d3
--- /dev/null
+++ b/gfx/tests/crashtests/1535657.html
@@ -0,0 +1,14 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252">
+ <style class="">
+ * {
+ transform: matrix(5062, 162, 20, 51, 144, 177) scale(45079, -40) scaleZ(19756) scaleX(120) matrix(117, 63, 50, 247, 8, 31);
+ min-height: 199rem ! important;
+ }
+ </style>
+</head><body>
+<ul></ul>
+
+
+
+</body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1541113.html b/gfx/tests/crashtests/1541113.html
new file mode 100644
index 0000000000..90e5ad9f31
--- /dev/null
+++ b/gfx/tests/crashtests/1541113.html
@@ -0,0 +1,7 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"></head><body><svg>
+<filter id="a">
+<feTile></feTile>
+</filter>
+<polyline filter="url(#a)" points="2,0 4,2 1,32767"></polyline>
+</svg></body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1547169.html b/gfx/tests/crashtests/1547169.html
new file mode 100644
index 0000000000..5fa6e7a00b
--- /dev/null
+++ b/gfx/tests/crashtests/1547169.html
@@ -0,0 +1,11 @@
+<style>
+.cl {
+ overflow-x: scroll;
+ contain: paint;
+ position: absolute;
+}
+</style>
+<q style="visibility: collapse">
+<table style="transform-style: preserve-3d">
+<tbody class="cl">
+<th style="rotate: -1deg 48 0 0">a</th>
diff --git a/gfx/tests/crashtests/1566206.html b/gfx/tests/crashtests/1566206.html
new file mode 100644
index 0000000000..e130072ab5
--- /dev/null
+++ b/gfx/tests/crashtests/1566206.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<head>
+ <style>
+ #init-rose {
+ animation: 20s infinite spin;
+ }
+
+ @keyframes spin {
+ 0% {transform: rotate(0deg);}
+ 100% {transform: rotate(359deg);}
+ }
+ </style>
+</head>
+<body>
+ <svg id="map" xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%">
+ <defs>
+ <g id="rose">
+ <g id="sL" stroke="#3f3f3f">
+ <line x1="0" y1="-10000000" x2="0" y2="10000000"/>
+ <line x1="-10000000" y1="0" x2="10000000" y2="0"/>
+ </g>
+ </g>
+ </defs>
+ <g id="initial" opacity=1>
+ <use xlink:href="#rose" id="init-rose" x="50%" y="50%"></use>
+ </g>
+ </svg>
+</body>
diff --git a/gfx/tests/crashtests/156882-1.html b/gfx/tests/crashtests/156882-1.html
new file mode 100644
index 0000000000..c861e0a839
--- /dev/null
+++ b/gfx/tests/crashtests/156882-1.html
@@ -0,0 +1,205 @@
+<HTML>
+<HEAD>
+<META http-equiv="Content-Type" content="charset=x-user-defined">
+<TITLE>Testcase</TITLE>
+</HEAD>
+<BODY>
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+&ouml;
+
+</BODY>
+</HTML> \ No newline at end of file
diff --git a/gfx/tests/crashtests/157320-1.html b/gfx/tests/crashtests/157320-1.html
new file mode 100644
index 0000000000..77119b6aa1
--- /dev/null
+++ b/gfx/tests/crashtests/157320-1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<title>foobidu</title>
+<meta http-equiv="Content-Type" content="text/html; charset=big5">
+
+</head>
+<body bgcolor="#C2C2C2" text="#000000" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0" >
+ <!-- mozilla doesn't like that! //-->
+ <!-- but this works!!! //-->
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1615091.html b/gfx/tests/crashtests/1615091.html
new file mode 100644
index 0000000000..9d92521546
--- /dev/null
+++ b/gfx/tests/crashtests/1615091.html
@@ -0,0 +1,11 @@
+<style>
+:not(track) {
+ transform-style: preserve-3d;
+ rotate: 43deg 0 52 1;
+ border-bottom: -moz-mac-menuselect solid;
+}
+:root { -webkit-perspective: 1px }
+</style>
+<textarea>aaaaaa</textarea>
+<summary style="rotate: 32deg -1 1 -1">aa</summary>
+
diff --git a/gfx/tests/crashtests/1615141.html b/gfx/tests/crashtests/1615141.html
new file mode 100644
index 0000000000..3613ef207c
--- /dev/null
+++ b/gfx/tests/crashtests/1615141.html
@@ -0,0 +1,13 @@
+<style>
+#a {
+ outline: solid;
+ mix-blend-mode: difference;
+}
+:not(animateTransform),
+feDiffuseLighting:nth-last-of-type(2) {
+ -webkit-transform-style: preserve-3d;
+ transform: translate3d(7px, 6px, 6px);
+ -webkit-perspective: 1px;
+</style>
+<ol id="a">
+
diff --git a/gfx/tests/crashtests/1620125.html b/gfx/tests/crashtests/1620125.html
new file mode 100644
index 0000000000..b1557bca36
--- /dev/null
+++ b/gfx/tests/crashtests/1620125.html
@@ -0,0 +1,12 @@
+<style>
+:root {
+ writing-mode: sideways-lr;
+ padding-right: -1;
+}
+.a {
+ font-style: oblique;
+ list-style-type: simp-chinese-formal;
+}
+<style>X</style>
+<ol class="a">
+<li>
diff --git a/gfx/tests/crashtests/1640401-1.html b/gfx/tests/crashtests/1640401-1.html
new file mode 100644
index 0000000000..3bf9c6efab
--- /dev/null
+++ b/gfx/tests/crashtests/1640401-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<!-- Test that SimSun, a default TTC font on Win10, is correctly handled by WR.
+ It may fail to generate a font descriptor and transmit as raw font data.
+ In some cases, WR did not properly expect to deal with said TTC font and
+ crashed as a result.
+-->
+<html><head>
+<style>
+@font-face {
+ font-family: "CrashMe";
+ src: local("SimSun");
+}
+
+body {
+ font-family: 'CrashMe';
+}
+
+h1 {
+ font-weight: 500;
+}
+
+input {
+ font-family: 'CrashMe';
+}
+
+</style>
+</head>
+<body>
+<h1>1</h1>
+<input placeholder="2">
+</body></html>
diff --git a/gfx/tests/crashtests/1647862.html b/gfx/tests/crashtests/1647862.html
new file mode 100644
index 0000000000..605827016b
--- /dev/null
+++ b/gfx/tests/crashtests/1647862.html
@@ -0,0 +1,14 @@
+<style>
+* {
+ scale: 70;
+}
+:not(colgroup) {
+ contain: content;
+ -webkit-perspective: 53px;
+ -webkit-border-radius: 2px 1px;
+}
+</style>
+<ul>
+<li>
+<textarea>
+
diff --git a/gfx/tests/crashtests/1647940.html b/gfx/tests/crashtests/1647940.html
new file mode 100644
index 0000000000..30d1a0d741
--- /dev/null
+++ b/gfx/tests/crashtests/1647940.html
@@ -0,0 +1,7 @@
+<script>
+window.onload = () => {
+ a.src = "data:video/webm;base64,GkXfowEAAAAAAAAfQoaBAUL3gQFC8oEEQvOBCEKChHdlYm1Ch4ECQoWBAhhTgGcBAAAAAAAB6BFNm3RALE27i1OrhBVJqWZTrIHfTbuMU6uEFlSua1OsggEwTbuMU6uEHFO7a1OsggHL7AEAAAAAAACkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVSalmAQAAAAAAAEUq17GDD0JATYCNTGF2ZjU3LjI5LjEwMVdBjUxhdmY1Ny4yOS4xMDFzpJBAb17Yv2oNAF1ZEESuco33RImIQFCAAAAAAAAWVK5rAQAAAAAAADyuAQAAAAAAADPXgQFzxYEBnIEAIrWcg3VuZIaFVl9WUDmDgQEj44OEAfygVeABAAAAAAAAB7CCAUC6gfAfQ7Z1AQAAAAAAAEfngQCjqYEAAICCSYNCABPwDvYAOCQcGFQAAFBh9jAAABML7AAATEnjdRwIJ+gAo5eBACEAhgBAkpwATEAABCasAABekcXgABxTu2sBAAAAAAAAEbuPs4EAt4r3gQHxggF48IED"
+}
+</script>
+<marquee style="column-count:3" width="1">
+<video id="a">
diff --git a/gfx/tests/crashtests/1650989-very-large-mask.html b/gfx/tests/crashtests/1650989-very-large-mask.html
new file mode 100644
index 0000000000..6673ce52f6
--- /dev/null
+++ b/gfx/tests/crashtests/1650989-very-large-mask.html
@@ -0,0 +1,10 @@
+<style>
+:root {
+ clip-path: url(#a);
+ transform: scale(65);
+ -webkit-box-shadow: -moz-activehyperlinktext 0px 0px 36px;
+ -webkit-mask-image: url()
+}
+</style>
+<svg>
+<clipPath id="a" transform="" />
diff --git a/gfx/tests/crashtests/1650990.html b/gfx/tests/crashtests/1650990.html
new file mode 100644
index 0000000000..c3ec3bdef1
--- /dev/null
+++ b/gfx/tests/crashtests/1650990.html
@@ -0,0 +1,18 @@
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
+#a {
+ -webkit-mask: url(data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=);
+ clip: rect(8px, 68px, 1px, 1px);
+}
+* {
+ position: fixed;
+}
+</style>
+<script>
+window.onload = () => {
+ a.show()
+}
+</script>
+</head><body><dialog id="a" open="">
+<font>x</font>
+</dialog></body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/1651882.html b/gfx/tests/crashtests/1651882.html
new file mode 100644
index 0000000000..ccf432f7e5
--- /dev/null
+++ b/gfx/tests/crashtests/1651882.html
@@ -0,0 +1,24 @@
+<html class="reftest-wait">
+<style>
+:not(th) {
+ -webkit-perspective: 5px;
+ -webkit-box-shadow: -moz-default-color 1px 0px;
+ -webkit-transform: translate(0px, 84px) scale(48);
+ mix-blend-mode: lighten;
+}
+</style>
+<script>
+window.onload = () => {
+ a.show()
+}
+setTimeout('document.documentElement.className = ""', 300);
+</script>
+<dialog id="a" style="border-left-style: hidden">
+<button>
+<dir>a</label>
+<details open>
+</details>
+</dir>
+</button>
+</dialog>
+</html>
diff --git a/gfx/tests/crashtests/1652750-deep-scene-stack.html b/gfx/tests/crashtests/1652750-deep-scene-stack.html
new file mode 100644
index 0000000000..11dd03f704
--- /dev/null
+++ b/gfx/tests/crashtests/1652750-deep-scene-stack.html
@@ -0,0 +1,24 @@
+<style>
+body {
+ transform-style: preserve-3d;
+}
+
+div {
+ height: 100px;
+ background-color: rgba(0, 255, 0, 0.1);;
+ transform: translateX(1px);
+}
+</style>
+<body>
+<script>
+ var div = document.createElement('div');
+ div.style.width = "1px";
+ for (var i = 2; i < 1000; i++) {
+ var container = document.createElement('div');
+ container.style.width = i + "px";
+ container.appendChild(div);
+ div = container;
+ }
+ document.body.appendChild(div);
+</script>
+</body>
diff --git a/gfx/tests/crashtests/1678938-1.html b/gfx/tests/crashtests/1678938-1.html
new file mode 100644
index 0000000000..c2d535c6dc
--- /dev/null
+++ b/gfx/tests/crashtests/1678938-1.html
@@ -0,0 +1,9 @@
+<style>
+:first-of-type {
+ filter: saturate(70%);
+ transform: skew(-224.96198773774648grad) matrix3d(79.00629819492802, -0.0, 43.223526565331326, -294.8479790240964, 269.7, 243.87676190037186, 238.83225166324632, 42.507227157006135, 634943.1, 208.66200905121616, 46.19831959954935, -285.43139932334543, -229.39776691490985, -126.46021751791264, 116.46, 137.77);
+ border-block-end-style: double;
+</style>
+<dl>
+<header>
+</html>
diff --git a/gfx/tests/crashtests/1679477-1.html b/gfx/tests/crashtests/1679477-1.html
new file mode 100644
index 0000000000..0d1a563b60
--- /dev/null
+++ b/gfx/tests/crashtests/1679477-1.html
@@ -0,0 +1,8 @@
+<style>
+* {
+ opacity: 0.4756117524753537;
+ -webkit-perspective: 2px;
+ rotate: 5deg 52 6 4;
+</style>
+<li>
+<iframe>
diff --git a/gfx/tests/crashtests/1685009-1.html b/gfx/tests/crashtests/1685009-1.html
new file mode 100644
index 0000000000..be00110767
--- /dev/null
+++ b/gfx/tests/crashtests/1685009-1.html
@@ -0,0 +1,12 @@
+<script>
+window.requestIdleCallback(() => {
+ window.close();
+})
+window.onload = () => {
+ a.value = "J."
+ b.submit()
+ window.requestAnimationFrame(() => { a.setRangeText("foo") })
+}
+</script>
+<textarea id="a" style="max-width: 0"></textarea>
+<form id="b" target="a">
diff --git a/gfx/tests/crashtests/199379-1.html b/gfx/tests/crashtests/199379-1.html
new file mode 100644
index 0000000000..ba4afe0b5e
--- /dev/null
+++ b/gfx/tests/crashtests/199379-1.html
@@ -0,0 +1,10 @@
+<html>
+ <head>
+ <title>bug 199379</title>
+ </head>
+ <body>
+ <form>
+ <input style="font-size:1000; width:32; height:34;" TYPE="SUBMIT" VALUE="link=basket&prod=96&tariff_id=999&desc=Fast Gate BASIC (ISDN 64K) &tariff=74,82&cp_id=5555&rating_tariff_id=0&parent_tariff_id=978&parent_product_id=&sItemDesc=Fast Gate BASIC (ISDN 64K) &package_id=0">
+ </form>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/206561-1.html b/gfx/tests/crashtests/206561-1.html
new file mode 100644
index 0000000000..f17cf11219
--- /dev/null
+++ b/gfx/tests/crashtests/206561-1.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title>bug 206561</title>
+ </head>
+ <body>
+ <div style="height: 100%; opacity: 0.8;"></div>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/248518-1.html b/gfx/tests/crashtests/248518-1.html
new file mode 100644
index 0000000000..b15c78612f
--- /dev/null
+++ b/gfx/tests/crashtests/248518-1.html
@@ -0,0 +1,7 @@
+<html><head>
+ <style type="text/css">
+ <!--
+ body{font: 10pt lucida;}
+ -->
+ </style>
+</head><body>Hello.</body></html>
diff --git a/gfx/tests/crashtests/306649-1.xml b/gfx/tests/crashtests/306649-1.xml
new file mode 100644
index 0000000000..c9861807c9
--- /dev/null
+++ b/gfx/tests/crashtests/306649-1.xml
@@ -0,0 +1 @@
+<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg xmlns="http://www.w3.org/2000/svg"> <g transform="scale(1e10)"> <rect x="0" y="0" width="400" height="200" rx="50" ry="50" fill="none" stroke="purple" stroke-width="30"/> </g> </svg> \ No newline at end of file
diff --git a/gfx/tests/crashtests/306902-1.xml b/gfx/tests/crashtests/306902-1.xml
new file mode 100644
index 0000000000..24e8c068a0
--- /dev/null
+++ b/gfx/tests/crashtests/306902-1.xml
@@ -0,0 +1,14 @@
+<?xml version='1.0'?>
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<body>
+
+<math xmlns='http://www.w3.org/1998/Math/MathML' display='block'><msup>
+
+XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
+
+</msup></math>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/333861-1.html b/gfx/tests/crashtests/333861-1.html
new file mode 100644
index 0000000000..694b06b3be
--- /dev/null
+++ b/gfx/tests/crashtests/333861-1.html
@@ -0,0 +1,18 @@
+<html><head>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<!--
+Copyright Georgi Guninski
+-->
+
+ <style>
+ button
+ {
+ font-size: 131131px;
+ }
+ </style>
+ </head><body>
+
+ <button>f00</button>
+</body></html>
diff --git a/gfx/tests/crashtests/334735-1.html b/gfx/tests/crashtests/334735-1.html
new file mode 100644
index 0000000000..f1a2c04a5a
--- /dev/null
+++ b/gfx/tests/crashtests/334735-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=UTF-8">
+<title>demo 1</title>
+</head><body>
+
+These unusual characters, &#1048713;,
+cause Firefox to crash &#x100089;
+
+
+</body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/345576-1.html b/gfx/tests/crashtests/345576-1.html
new file mode 100644
index 0000000000..547d4606cd
--- /dev/null
+++ b/gfx/tests/crashtests/345576-1.html
@@ -0,0 +1,6 @@
+<html>
+<head>
+</head>
+<body>
+<font
+font-weight=5555555555555555555555555555555555555555555555555555555555555555555555 \ No newline at end of file
diff --git a/gfx/tests/crashtests/345629-1.html b/gfx/tests/crashtests/345629-1.html
new file mode 100644
index 0000000000..0706b9d0d0
--- /dev/null
+++ b/gfx/tests/crashtests/345629-1.html
@@ -0,0 +1,7 @@
+<html>
+<head>
+</head>
+<body>
+<font font-weight="467591">x</font>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/348462-1.html b/gfx/tests/crashtests/348462-1.html
new file mode 100644
index 0000000000..e49b891587
--- /dev/null
+++ b/gfx/tests/crashtests/348462-1.html
@@ -0,0 +1,11 @@
+<html>
+<head>
+<style>.classname { font-size: 2666.3423423423423424 }</style>
+<title>bla crash</title>
+</head>
+
+<body>
+<p class="classname">crash!</p>
+</body>
+
+</html>
diff --git a/gfx/tests/crashtests/348462-2.html b/gfx/tests/crashtests/348462-2.html
new file mode 100644
index 0000000000..5efbaa5b0f
--- /dev/null
+++ b/gfx/tests/crashtests/348462-2.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom() {
+ document.body.appendChild(document.createTextNode(String.fromCharCode(25261)))
+ document.body.style.fontSize = '8388608px';
+}
+
+</script>
+</head>
+<body onload="boom()"></body>
+</html>
diff --git a/gfx/tests/crashtests/366643.html b/gfx/tests/crashtests/366643.html
new file mode 100644
index 0000000000..b8ce73a1f5
--- /dev/null
+++ b/gfx/tests/crashtests/366643.html
@@ -0,0 +1,7 @@
+<html><head>
+ <title>Uniscribe::Itemize crash</title>
+</head><body>
+
+x.&#8205;x.&#8205;x
+
+</body></html>
diff --git a/gfx/tests/crashtests/369688-1.html b/gfx/tests/crashtests/369688-1.html
new file mode 100644
index 0000000000..f71c8b091d
--- /dev/null
+++ b/gfx/tests/crashtests/369688-1.html
@@ -0,0 +1,19 @@
+<html class="reftest-wait">
+
+<head>
+<script>
+function boom()
+{
+ document.body.style.fontSizeAdjust = 0xffffffff;
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+
+<body onload="setTimeout(boom, 30);">
+
+<p>Foo</p>
+
+</body>
+
+</html>
diff --git a/gfx/tests/crashtests/369947-1.html b/gfx/tests/crashtests/369947-1.html
new file mode 100644
index 0000000000..efb0aed9ca
--- /dev/null
+++ b/gfx/tests/crashtests/369947-1.html
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+</head>
+
+<body>
+
+<pre>bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar bar &rho; foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo foo </pre>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/372094-1.xhtml b/gfx/tests/crashtests/372094-1.xhtml
new file mode 100644
index 0000000000..91b81ac58b
--- /dev/null
+++ b/gfx/tests/crashtests/372094-1.xhtml
@@ -0,0 +1,45 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<script>
+<![CDATA[
+
+function init()
+{
+ setTimeout(function()
+ {
+ targetWindow = window.frames[0];
+ targetDocument = targetWindow.document;
+ targetDocument.body.appendChild(targetDocument.importNode(document.getElementById('rootish'), true));
+ targetDocument.designMode = 'on';
+ setTimeout(boom, 30);
+ }, 30);
+}
+
+function boom()
+{
+ var r = targetDocument.createRange();
+ r.setStart(targetDocument.getElementById("bar"), 0);
+ r.setEnd(targetDocument.getElementById("baz").firstChild, 0);
+ targetWindow.getSelection().addRange(r);
+
+ targetDocument.execCommand("indent", false, null);
+
+ document.documentElement.removeAttribute("class");
+}
+
+]]>
+</script>
+</head>
+
+<body onload="init()">
+
+ <iframe srcdoc="<html></html>" style="width: 95%; height: 500px;"/>
+
+<div id="rootish">
+ <div>Foo</div>
+ <div id="bar">Bar</div>
+ <div><select><option id="baz">baz</option></select></div>
+</div>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/376627-1.html b/gfx/tests/crashtests/376627-1.html
new file mode 100644
index 0000000000..44319efedd
--- /dev/null
+++ b/gfx/tests/crashtests/376627-1.html
@@ -0,0 +1,3 @@
+<html>
+<body>
+&#01;
diff --git a/gfx/tests/crashtests/377231-1.html b/gfx/tests/crashtests/377231-1.html
new file mode 100644
index 0000000000..c3e52284c4
--- /dev/null
+++ b/gfx/tests/crashtests/377231-1.html
@@ -0,0 +1 @@
+<div><span>&#1741;</span><span>&#8232;</span><span>&#1994;</span></div>
diff --git a/gfx/tests/crashtests/377232-1.xhtml b/gfx/tests/crashtests/377232-1.xhtml
new file mode 100644
index 0000000000..4ab81eca0d
--- /dev/null
+++ b/gfx/tests/crashtests/377232-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="font-family: arial">
+<div>&#1050;&#769;</div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/377461-1.xhtml b/gfx/tests/crashtests/377461-1.xhtml
new file mode 100644
index 0000000000..adeaaaccfc
--- /dev/null
+++ b/gfx/tests/crashtests/377461-1.xhtml
@@ -0,0 +1,16 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div");
+ div.textContent = "\uFDDE\r\uFDDE";
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+<div id="div"></div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/383473-1.html b/gfx/tests/crashtests/383473-1.html
new file mode 100644
index 0000000000..12d6d0f5bc
--- /dev/null
+++ b/gfx/tests/crashtests/383473-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+<span style="font-size:72px; font-size-adjust:24">X</span>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/383872-1.svg b/gfx/tests/crashtests/383872-1.svg
new file mode 100644
index 0000000000..2d32753f76
--- /dev/null
+++ b/gfx/tests/crashtests/383872-1.svg
@@ -0,0 +1,19 @@
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:html="http://www.w3.org/1999/xhtml">
+
+<html:style>
+
+#para {
+ width: 30px;
+ height: 0.5px;
+ background: url("data:image/gif;base64,R0lGODlhMQA8ANX%2FAJ22hRgcFI6keHqNZzpDMZ%2B2hqO4iR8jGpythSgsIa28kwoLCGJrULzCoFBQPsfFqb20hRMRDMq4is62hbeidjMuJNC3iJ%2BMaIJyVdK6jdi%2Fm6KUgNfJtSYhGkM5LsetluvGtebLvq2PiPHLwsDAwPbMx7%2BbmdKpqAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACQALAAAAAAxADwAAAb%2FQJJwSCwaj8TSqIRsOkmlKNNZCj0UBoPiEZo%2BjVUO5%2FFoNB6cUGgJJYYaBoAcUDCcQ99hiQMvzP91CmYNHEwjDwZ%2Bf3J1HHkkHApzA5SVAweYBwQMCGdwAgwVCaOjFQwABnhPIw1%2BAwkEsbEHC6dyA7F1BaAMAosCDg4ADV8ccbgDiwnJiw62zouTCQAKXkclDaAEzH%2FL0QMOoguk24soqCNOIwoM5dHei%2B22kwTm6E4hAvDR%2FJfc3fYUpGsSAhq%2Fg7gOTvtzTkEXggsP8hPw748wAJXO2SmEJEREiSAPOiCwYFsdA46OeAzJEiSyLCmLHPrYsuYcZzBlRjLw0UFF%2F5sICTRKgkhOxEu%2BgIbENXSIsTmmfAVTypIpyiHr5DhAwVXcT6qLEiqIGeJYvW7zwPJjcNGhkLIANoUlkBSjWgan8CYt8EAIBzkH6s5hm%2FSAWgEBEjgQgIJZNUiARRoGEEAwUMa3ygmEDGByPzls1QKAlzAVJD%2BeQV4Eu8%2FwVbipRUvEPMcwXxJZ3ck%2BCCzt5AZRGgBgQHP3n9B%2FbG9%2BOqCycXM0BSjHUwJRAQYHAmD6%2BrwdHbe4r%2FjR%2FfyXu81C9kgiXx60dzrEiBZg3z4WM9NFytIH4JNRgf8A1qRbfDIpsF85BkCg4IJZKPIHRXMIQBcqDQxkxAOERbONARtAMP%2FBhx9aIAEKE8oBTIl4afGAhUaEYOBqtQmggDMg1ojCVKAFk4kDCHDABj4IjMTNKwloQIEHNYKIggQJkGhfFiqy%2BISL7WQnSwUaTFBBkh%2BiMIEGDWywgQlqqCFlHmFcIeYGHTBwwZZcennmI1R4cYIIb3I5gZd09nmElnry6eegQgAapwWEEmpokihkkOigi9qYwZyPNhGpkpNW%2BsgIRwZqAaWaFqEBXoFOAEKoX1DgAAWlnopqEyBg4ACIFtRYwQUavHrECCCoesGHF9R66wRvWuDqoCcMdIIJzDbL7AUOYPAhBB182EECrLJ1gasjNHvCF6FUUEEw5JLrQQSzTiCRa7odYIDkBB5cK%2B64wVTgwb34eoBBHrzCGwGxFUTwLrzQ%2FkpBBRiAYM2jxHXQgb72YvAmq%2Bo6AC2rBX84AqiriJCAh9B6cEEGGViAQQK%2FQiDyyfe66eGHmfqZpQYlW6wBG7y%2BeauswF4gKwrzTmBBrroKMYIFFyQgsdAhZqDBmhpocGzRJIDwQQdOU%2B2nCZUGAQA7");
+}
+
+</html:style>
+
+<foreignObject width="100" height="100" x="100" y="100" transform="scale(.7,.7)">
+ <html:div>
+ <html:p id="para">Foo</html:p>
+ </html:div>
+</foreignObject>
+
+</svg>
diff --git a/gfx/tests/crashtests/385228-1.svg b/gfx/tests/crashtests/385228-1.svg
new file mode 100644
index 0000000000..ec448b5b30
--- /dev/null
+++ b/gfx/tests/crashtests/385228-1.svg
@@ -0,0 +1,22 @@
+<svg xmlns="http://www.w3.org/2000/svg" onload="setTimeout(boom, 10);" class="reftest-wait">
+
+<script type="text/javascript">
+
+function boom()
+{
+ var ttt = document.getElementById("ttt");
+ ttt.appendChild(document.createTextNode("Pattern on stroke"));
+ ttt.appendChild(document.createTextNode("\n"));
+ ttt.appendChild(document.createTextNode("\n"));
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+
+
+<text id="ttt" stroke="url(#pat2)" x="25" y="275"></text>
+
+<pattern id="pat2" width="20" height="1000"><g/></pattern>
+
+</svg>
diff --git a/gfx/tests/crashtests/385228-2.svg b/gfx/tests/crashtests/385228-2.svg
new file mode 100644
index 0000000000..82586fcfaf
--- /dev/null
+++ b/gfx/tests/crashtests/385228-2.svg
@@ -0,0 +1,20 @@
+<svg version="1.1" baseProfile="basic" xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 480 360" onload="yaa();" class="reftest-wait">
+
+<g id="g">
+ <rect width="440" height="340" fill="url(#pat1)"/>
+ <pattern id="pat1" width="20" height="20"><rect/></pattern>
+</g>
+
+<script type="text/javascript">
+
+function yaa()
+{
+ var g = document.getElementById("g");
+ document.documentElement.removeChild(g);
+ document.documentElement.appendChild(g);
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</svg>
diff --git a/gfx/tests/crashtests/385289-1.xhtml b/gfx/tests/crashtests/385289-1.xhtml
new file mode 100644
index 0000000000..666756c016
--- /dev/null
+++ b/gfx/tests/crashtests/385289-1.xhtml
@@ -0,0 +1,30 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ var mss = document.getElementById("mss");
+
+ var j = document.createTextNode("j");
+ var comb = document.createTextNode("\u0302");
+
+ mss.appendChild(j);
+ mss.appendChild(comb);
+}
+</script>
+</head>
+
+<body onload="boom()">
+
+<div>
+ <math xmlns="http://www.w3.org/1998/Math/MathML" display="block">
+ <msub id="mss">
+ <mi>v</mi>
+ <mn>1</mn>
+ </msub>
+ </math>
+</div>
+
+</body>
+
+</html>
diff --git a/gfx/tests/crashtests/385417-1.html b/gfx/tests/crashtests/385417-1.html
new file mode 100644
index 0000000000..0bf70ec340
--- /dev/null
+++ b/gfx/tests/crashtests/385417-1.html
@@ -0,0 +1 @@
+<html><body>&#x22A3;&zwnj;</body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/385417-2.html b/gfx/tests/crashtests/385417-2.html
new file mode 100644
index 0000000000..f94f21e691
--- /dev/null
+++ b/gfx/tests/crashtests/385417-2.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+
+<div><span style="text-transform: capitalize; font-variant: small-caps;">
+x</span></div>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/385423-1.html b/gfx/tests/crashtests/385423-1.html
new file mode 100644
index 0000000000..748776c309
--- /dev/null
+++ b/gfx/tests/crashtests/385423-1.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div")
+ div.appendChild(document.createTextNode(String.fromCharCode(0x076F) + String.fromCharCode(13) + String.fromCharCode(0x076F)));
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</head>
+<body onload="setTimeout(boom, 10);">
+<div style="text-transform: lowercase" id="div">&#x76F;&#13;&#x76F;</div>
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/385423-2.html b/gfx/tests/crashtests/385423-2.html
new file mode 100644
index 0000000000..7de8bdaaf6
--- /dev/null
+++ b/gfx/tests/crashtests/385423-2.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div")
+ div.appendChild(document.createTextNode(String.fromCharCode(0x076F) + String.fromCharCode(13) + String.fromCharCode(0x076F)));
+
+ document.documentElement.removeAttribute("class");
+}
+</script>
+
+</head>
+<body onload="setTimeout(boom, 10);">
+<div style="text-transform: lowercase" id="div"></div>
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/385719-1.html b/gfx/tests/crashtests/385719-1.html
new file mode 100644
index 0000000000..3081dcc04e
--- /dev/null
+++ b/gfx/tests/crashtests/385719-1.html
@@ -0,0 +1 @@
+<div>-<span>&zwj;</span>&#xB235;</div> \ No newline at end of file
diff --git a/gfx/tests/crashtests/389326-1-inner.xhtml b/gfx/tests/crashtests/389326-1-inner.xhtml
new file mode 100644
index 0000000000..30236cf8b3
--- /dev/null
+++ b/gfx/tests/crashtests/389326-1-inner.xhtml
@@ -0,0 +1,29 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+<![CDATA[
+
+function boom()
+{
+ var f = document.getElementById("f");
+ var s = document.getElementById("s");
+ s.insertBefore(f, s.firstChild);
+
+ setTimeout(rel, 200);
+}
+
+function rel()
+{
+ location.reload();
+}
+
+]]>
+</script>
+
+</head>
+
+<body onload="boom();">
+<font id="f"><b>2</b>"</font>
+<b>"<span id="s">="<b></b></span></b>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/389326-1.html b/gfx/tests/crashtests/389326-1.html
new file mode 100644
index 0000000000..2d9451596a
--- /dev/null
+++ b/gfx/tests/crashtests/389326-1.html
@@ -0,0 +1,9 @@
+<html class="reftest-wait">
+<head>
+<script>
+setTimeout('document.documentElement.className = ""', 1000);
+</script>
+<body>
+<iframe src="389326-1-inner.xhtml"></iframe>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/390476.html b/gfx/tests/crashtests/390476.html
new file mode 100644
index 0000000000..069369fe71
--- /dev/null
+++ b/gfx/tests/crashtests/390476.html
@@ -0,0 +1,13 @@
+<html><head>
+<title>Crash [@ _moz_cairo_win32_scaled_font_select_font] with negative font-size in canvas</title>
+<script>
+ var ctx = document.createElement('canvas').getContext('2d');
+ ctx.translate(100, 100);
+ ctx.mozTextStyle = "-14pt sans serif";
+ ctx.mozDrawText('text');
+</script>
+</head>
+<body>
+<canvas id="canvas"></canvas>
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/393746-1.xhtml b/gfx/tests/crashtests/393746-1.xhtml
new file mode 100644
index 0000000000..feba6a3c08
--- /dev/null
+++ b/gfx/tests/crashtests/393746-1.xhtml
@@ -0,0 +1,14 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<script>
+function boom()
+{
+ var text = document.createTextNode("t");
+ document.getElementById("foo").appendChild(text);
+}
+</script>
+</head>
+
+<body onload="boom();" dir="rtl"><b id="foo"></b> x </body>
+
+</html>
diff --git a/gfx/tests/crashtests/393749-1.html b/gfx/tests/crashtests/393749-1.html
new file mode 100644
index 0000000000..edaba0eb28
--- /dev/null
+++ b/gfx/tests/crashtests/393749-1.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ var het = document.createTextNode(String.fromCharCode(0x05D7)); // hebrew letter het
+ document.getElementById("s").appendChild(het);
+}
+</script>
+</head>
+
+<body onload="boom();" dir="rtl">
+
+<div><span id="s">A</span><span>&nbsp;</span>B</div>
+
+</body>
+
+</html>
diff --git a/gfx/tests/crashtests/393822-1.html b/gfx/tests/crashtests/393822-1.html
new file mode 100644
index 0000000000..e763632dfb
--- /dev/null
+++ b/gfx/tests/crashtests/393822-1.html
@@ -0,0 +1,32 @@
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<head>
+<script>
+
+var ff;
+var jj;
+
+function boom()
+{
+ ff = document.createElement("font");
+ ff.setAttribute("size", "-2");
+ ff.appendChild(document.createTextNode("G"));
+
+ jj = document.getElementById("jj");
+
+ jj.appendChild(ff);
+ setTimeout(boom2, 30);
+}
+
+function boom2()
+{
+ jj.removeChild(ff);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="boom();" dir="rtl"><div id="jj">h </div></body>
+
+</html>
diff --git a/gfx/tests/crashtests/394246-1.html b/gfx/tests/crashtests/394246-1.html
new file mode 100644
index 0000000000..e09facb8b2
--- /dev/null
+++ b/gfx/tests/crashtests/394246-1.html
@@ -0,0 +1,16 @@
+<html>
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div");
+ div.innerHTML = div.innerHTML.slice(1);
+}
+</script>
+</head>
+<body onload="boom();">
+
+<div id="div">t
+&#769;</div>
+
+</body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/394246-2.html b/gfx/tests/crashtests/394246-2.html
new file mode 100644
index 0000000000..e5fe8c1754
--- /dev/null
+++ b/gfx/tests/crashtests/394246-2.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+<head>
+<script>
+function boom()
+{
+ var div = document.getElementById("div");
+ div.innerHTML = div.innerHTML.slice(1);
+ setTimeout(c, 30);
+}
+
+function c()
+{
+ div.innerHTML = div.innerHTML.slice(1);
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="boom();">
+
+<div id="div">t
+&#769;</div>
+
+</body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/394384-1.html b/gfx/tests/crashtests/394384-1.html
new file mode 100644
index 0000000000..279c473a06
--- /dev/null
+++ b/gfx/tests/crashtests/394384-1.html
@@ -0,0 +1,26 @@
+<html class="reftest-wait">
+<head>
+<script>
+
+function boom()
+{
+ var t1 = document.createTextNode(String.fromCharCode(0x2011) + String.fromCharCode(13));
+ document.body.appendChild(t1);
+
+ setTimeout(boom2, 30);
+}
+
+function boom2()
+{
+ var letterA = document.createTextNode(String.fromCharCode(65));
+ document.body.appendChild(letterA);
+
+ document.documentElement.removeAttribute("class");
+}
+
+</script>
+</head>
+
+<body onload="setTimeout(boom, 50);"></body>
+
+</html>
diff --git a/gfx/tests/crashtests/394751.xhtml b/gfx/tests/crashtests/394751.xhtml
new file mode 100644
index 0000000000..35a65af09c
--- /dev/null
+++ b/gfx/tests/crashtests/394751.xhtml
@@ -0,0 +1,3 @@
+<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">XML Parsing Error: not well-formed
+Location: file:///C:/Documents%20and%20Settings/mw/Bureaublad/crashzone/crash1.xhtml
+Line Number 3, Column 42964:<sourcetext>Line Number 3, Column 42134:&lt;sourcetext&gt;Line Number 3, Column 41312:&amp;lt;sourcetext&amp;gt;Line Number 3, Column 40498:&amp;amp;lt;sourcetext&amp;amp;gt;Line Number 3, Column 39692:&amp;amp;amp;lt;sourcetext&amp;amp;amp;gt;Line Number 3, Column 38894:&amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;gt;Line Number 3, Column 38104:&amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;gt;Line Number 3, Column 37322:&amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 36548:&amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 35782:&amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 35024:&amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 34274:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 33532:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 32798:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 32072:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 31354:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 30644:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 29942:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 29248:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 28562:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 27884:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 27214:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 26552:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 25898:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 25252:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 24614:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 23984:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 23362:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 22748:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 22142:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 21544:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 20954:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 20372:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 19798:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 19232:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 18674:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 18124:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 17582:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 17048:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 16522:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 16004:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 15494:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 14992:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 14498:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 14012:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 13534:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 13064:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 12602:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 12148:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 11702:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 11264:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 10834:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 10412:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 9999:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 9594:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 9197:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 8808:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 8427:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 8054:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 7689:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 7332:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 6983:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 6642:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 6309:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5984:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5667:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5358:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 5057:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 4764:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 4479:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 4202:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3933:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3672:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3419:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 3174:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2937:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2708:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2487:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2274:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 2069:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1872:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1683:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1502:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1329:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1164:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 1007:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 3, Column 859:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;Line Number 1, Column 743:&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;sourcetext&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:mathml="http://www.w3.org/1998/Math/MathML" xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:wairole="http://www.w3.org/2005/01/wai-rdf/GUIRoleTaxonomy#" xmlns:aaa="http://www.w3.org/2005/07/aaa" xmlns:xforms="http://www.w3.org/2002/xforms"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;9&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;svg:foreignObject x="0" y="0" width="100%" height="100%"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;wbr style="" id="a" name="b"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;area style="" id="b" name="b"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;=&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/area&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;marquee style="" id="b" name="b"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/marquee&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/wbr&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/svg:foreignObject&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;script style="" id="a" name="c"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;^&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;hx style="" id="b" name="c"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;/hx&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;gt;&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;lt;a style="" id="b" name="d"&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;aa </sourcetext></parsererror> \ No newline at end of file
diff --git a/gfx/tests/crashtests/395335-1.xhtml b/gfx/tests/crashtests/395335-1.xhtml
new file mode 100644
index 0000000000..d9b73edc16
--- /dev/null
+++ b/gfx/tests/crashtests/395335-1.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+<head>
+<script>
+
+function boom()
+{
+ document.getElementById("tree").firstChild.data = "xyz";
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+
+<xul:tree id="tree">a</xul:tree>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/395458-1.html b/gfx/tests/crashtests/395458-1.html
new file mode 100644
index 0000000000..80fc55178b
--- /dev/null
+++ b/gfx/tests/crashtests/395458-1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="direction: rtl;">
+&#30;
+</body>
+</html>
diff --git a/gfx/tests/crashtests/396321-1.svg b/gfx/tests/crashtests/396321-1.svg
new file mode 100644
index 0000000000..6032921346
--- /dev/null
+++ b/gfx/tests/crashtests/396321-1.svg
@@ -0,0 +1,5 @@
+<svg xmlns="http://www.w3.org/2000/svg">
+
+<text>&#x202B;x</text>
+
+</svg>
diff --git a/gfx/tests/crashtests/398042-1.xhtml b/gfx/tests/crashtests/398042-1.xhtml
new file mode 100644
index 0000000000..33c18a6052
--- /dev/null
+++ b/gfx/tests/crashtests/398042-1.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style id="s">
+maligngroup { white-space: pre; }
+</style>
+</head>
+
+<body onload="document.getElementById('s').disabled = true;">
+<ms xmlns="http://www.w3.org/1998/Math/MathML"><maligngroup>
+
+ </maligngroup></ms>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/398042-2.xhtml b/gfx/tests/crashtests/398042-2.xhtml
new file mode 100644
index 0000000000..49b40673a1
--- /dev/null
+++ b/gfx/tests/crashtests/398042-2.xhtml
@@ -0,0 +1,13 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<style id="s">
+maligngroup { white-space: pre; }
+</style>
+</head>
+
+<body onload="document.getElementById('s').disabled = true;">
+<ms xmlns="http://www.w3.org/1998/Math/MathML"><maligngroup>
+
+</maligngroup></ms>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/402307-1.html b/gfx/tests/crashtests/402307-1.html
new file mode 100644
index 0000000000..0f9c2941d0
--- /dev/null
+++ b/gfx/tests/crashtests/402307-1.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body style="font-size: 15000px; word-spacing: 10px;">
+
+ABCDE&#xA2C9;
+&zwj;&#x9BB8;&#x9143;
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/403352.html b/gfx/tests/crashtests/403352.html
new file mode 100644
index 0000000000..1425665635
--- /dev/null
+++ b/gfx/tests/crashtests/403352.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script>
+function bounce()
+{
+ var b = document.body;
+ var de = document.documentElement;
+ de.removeChild(b);
+ de.appendChild(b);
+}
+</script>
+</head>
+<body onload="bounce();" style="font-size: 10000%;">&#8239;&rlm;x&#861;</body>
+</html>
diff --git a/gfx/tests/crashtests/403464-1.html b/gfx/tests/crashtests/403464-1.html
new file mode 100644
index 0000000000..459486539c
--- /dev/null
+++ b/gfx/tests/crashtests/403464-1.html
@@ -0,0 +1,134 @@
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html>
+<head>
+<title></title>
+
+<script type="text/javascript" src="jquery.js"></script>
+<script type="text/javascript">
+</script>
+
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></meta>
+<style type="text/css">
+body { margin: 0 4em; font-family: sans-serif; font-size:xx-large;}
+#content { width: 800px; margin: 0 auto; padding: 10em 0;}
+</style>
+
+</head>
+<body>
+
+<div id="content">
+
+ừ̴̵̶̷̸̡̢̧̨̖̗̘̙̜̝̞̟̠̣̤̥̦̩̪̫̬̭̮̯̰̱̲̳̹̺̻̼͇͈͉͍͎́̂̃̄̅̆̇̈̉̊̋̌̍̎̏̐̑̒̓̔̽̾̿̀́͂̓̈́͆͊͋͌̕̚ͅ͏͓͔͕͖͙͚͐͑͒͗͛ͣ͘͜͟͢͝͞͠͡
+</div><!-- #content -->
+
+
+<pre>
+u LATIN SMALL LETTER U
+̀ COMBINING GRAVE ACCENT
+́ COMBINING ACUTE ACCENT
+̂ COMBINING CIRCUMFLEX ACCENT
+̃ COMBINING TILDE
+̄ COMBINING MACRON
+̅ COMBINING OVERLINE
+̆ COMBINING BREVE
+̇ COMBINING DOT ABOVE
+̈ COMBINING DIAERESIS
+̉ COMBINING HOOK ABOVE
+̊ COMBINING RING ABOVE
+̋ COMBINING DOUBLE ACUTE ACCENT
+̌ COMBINING CARON
+̍ COMBINING VERTICAL LINE ABOVE
+̎ COMBINING DOUBLE VERTICAL LINE ABOVE
+̏ COMBINING DOUBLE GRAVE ACCENT
+̐ COMBINING CANDRABINDU
+̑ COMBINING INVERTED BREVE
+̒ COMBINING TURNED COMMA ABOVE
+̓ COMBINING COMMA ABOVE
+̔ COMBINING REVERSED COMMA ABOVE
+̕ COMBINING COMMA ABOVE RIGHT
+̖ COMBINING GRAVE ACCENT BELOW
+̗ COMBINING ACUTE ACCENT BELOW
+̘ COMBINING LEFT TACK BELOW
+̙ COMBINING RIGHT TACK BELOW
+̚ COMBINING LEFT ANGLE ABOVE
+̛ COMBINING HORN
+̜ COMBINING LEFT HALF RING BELOW
+̝ COMBINING UP TACK BELOW
+̞ COMBINING DOWN TACK BELOW
+̟ COMBINING PLUS SIGN BELOW
+̠ COMBINING MINUS SIGN BELOW
+̡ COMBINING PALATALIZED HOOK BELOW
+̢ COMBINING RETROFLEX HOOK BELOW
+̣ COMBINING DOT BELOW
+̤ COMBINING DIAERESIS BELOW
+̥ COMBINING RING BELOW
+̦ COMBINING COMMA BELOW
+̧ COMBINING CEDILLA
+̨ COMBINING OGONEK
+̩ COMBINING VERTICAL LINE BELOW
+̪ COMBINING BRIDGE BELOW
+̫ COMBINING INVERTED DOUBLE ARCH BELOW
+̬ COMBINING CARON BELOW
+̭ COMBINING CIRCUMFLEX ACCENT BELOW
+̮ COMBINING BREVE BELOW
+̯ COMBINING INVERTED BREVE BELOW
+̰ COMBINING TILDE BELOW
+̱ COMBINING MACRON BELOW
+̲ COMBINING LOW LINE
+̳ COMBINING DOUBLE LOW LINE
+̴ COMBINING TILDE OVERLAY
+̵ COMBINING SHORT STROKE OVERLAY
+̶ COMBINING LONG STROKE OVERLAY
+̷ COMBINING SHORT SOLIDUS OVERLAY
+̸ COMBINING LONG SOLIDUS OVERLAY
+̹ COMBINING RIGHT HALF RING BELOW
+̺ COMBINING INVERTED BRIDGE BELOW
+̻ COMBINING SQUARE BELOW
+̼ COMBINING SEAGULL BELOW
+̽ COMBINING X ABOVE
+̾ COMBINING VERTICAL TILDE
+̿ COMBINING DOUBLE OVERLINE
+̀ COMBINING GRAVE TONE MARK
+́ COMBINING ACUTE TONE MARK
+͂ COMBINING GREEK PERISPOMENI
+̓ COMBINING GREEK KORONIS
+̈́ COMBINING GREEK DIALYTIKA TONOS
+ͅ COMBINING GREEK YPOGEGRAMMENI
+͆ COMBINING BRIDGE ABOVE
+͇ COMBINING EQUALS SIGN BELOW
+͈ COMBINING DOUBLE VERTICAL LINE BELOW
+͉ COMBINING LEFT ANGLE BELOW
+͊ COMBINING NOT TILDE ABOVE
+͋ COMBINING HOMOTHETIC ABOVE
+͌ COMBINING ALMOST EQUAL TO ABOVE
+͍ COMBINING LEFT RIGHT ARROW BELOW
+͎ COMBINING UPWARDS ARROW BELOW
+͏ COMBINING GRAPHEME JOINER
+͐ COMBINING RIGHT ARROWHEAD ABOVE
+͑ COMBINING LEFT HALF RING ABOVE
+͒ COMBINING FERMATA
+͓ COMBINING X BELOW
+͔ COMBINING LEFT ARROWHEAD BELOW
+͕ COMBINING RIGHT ARROWHEAD BELOW
+͖ COMBINING RIGHT ARROWHEAD AND UP ARROWHEAD BELOW
+͗ COMBINING RIGHT HALF RING ABOVE
+͘ COMBINING DOT ABOVE RIGHT
+͙ COMBINING ASTERISK BELOW
+͚ COMBINING DOUBLE RING BELOW
+͛ COMBINING ZIGZAG ABOVE
+͜ COMBINING DOUBLE BREVE BELOW
+͝ COMBINING DOUBLE BREVE
+͞ COMBINING DOUBLE MACRON
+͟ COMBINING DOUBLE MACRON BELOW
+͠ COMBINING DOUBLE TILDE
+͡ COMBINING DOUBLE INVERTED BREVE
+͢ COMBINING DOUBLE RIGHTWARDS ARROW BELOW
+ͣ COMBINING LATIN SMALL LETTER A
+
+</pre>
+<p>Friends and neighbors, this terrifying piece of Unicode technology was created by Mr SBP of <a href="http://inamidst.com/odds/supercombiner">inamidst.com</a></p>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/404112-1.html b/gfx/tests/crashtests/404112-1.html
new file mode 100644
index 0000000000..aa5d3fea64
--- /dev/null
+++ b/gfx/tests/crashtests/404112-1.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Google</title>
+</head>
+<style>body,td,a,p,.h{font-family:arial,sans-serif}.h{color:#3366cc}.q{color:#00c}.ts td{padding:0}.ts{border-collapse:collapse}#gbar{float:left;font-weight:bold;height:22px;padding-left:2px}#gbh{border-top:1px solid #c9d7f1;font-size:0;height:0;position:absolute;right:0;top:24px;width:200%}#gbi{background:#fff;border:1px solid;border-color:#c9d7f1 #36c #36c #a2bae7;font-size:13px;top:24px;z-index:1000}#guser{padding-bottom:7px !important}#gbar,#guser{font-size:13px;padding-top:1px !important}@media all{.gb1,.gb3{height:22px;margin-right:.73em;vertical-align:top}}#gbi,.gb2{display:none;position:absolute;width:8em}.gb2{z-index:1001}#gbar a,#gbar a:active,#gbar a:visited{color:#00c;font-weight:normal}.gb2 a,.gb3 a{text-decoration:none}.gb2 a{display:block;padding:.2em .5em}#gbar .gb2 a:hover{background:#36c;color:#fff}
+</style>
+<body>
+<div id="gbh"></div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/404112-2.html b/gfx/tests/crashtests/404112-2.html
new file mode 100644
index 0000000000..a3cb47fa43
--- /dev/null
+++ b/gfx/tests/crashtests/404112-2.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>bug 404112</title>
+</head>
+<body>
+<div style="font-size: 0"></div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/405268-1.xhtml b/gfx/tests/crashtests/405268-1.xhtml
new file mode 100644
index 0000000000..ecb6ecf218
--- /dev/null
+++ b/gfx/tests/crashtests/405268-1.xhtml
@@ -0,0 +1,20 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title></title>
+<script type="text/javascript">
+//<![CDATA[
+function boom()
+{
+ var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
+ document.body.appendChild(div);
+ div.style.fontSize = '68719476736px';
+ div.appendChild(document.createTextNode(String.fromCharCode(0)));
+}
+//]]>
+</script>
+</head>
+
+<body onload="boom()">
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/407761-1.html b/gfx/tests/crashtests/407761-1.html
new file mode 100644
index 0000000000..96e9597f79
--- /dev/null
+++ b/gfx/tests/crashtests/407761-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html> <head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<title></title>
+</head>
+<body>
+<span style="font-family: Verdana">IIIIIiiii'm dumb. ಠ_ಠ</span>
+</body> </html>
diff --git a/gfx/tests/crashtests/407842.html b/gfx/tests/crashtests/407842.html
new file mode 100644
index 0000000000..a90c13b3d5
--- /dev/null
+++ b/gfx/tests/crashtests/407842.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
+
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<title>Gecko Crash Demo</title>
+</head>
+
+<body>
+
+<div>
+ <span style="font-size:463.25em"><a href="#">Hello World!</a></span>
+</div>
+
+</body>
+</html>
+
diff --git a/gfx/tests/crashtests/408754-1.html b/gfx/tests/crashtests/408754-1.html
new file mode 100644
index 0000000000..4e716d682c
--- /dev/null
+++ b/gfx/tests/crashtests/408754-1.html
@@ -0,0 +1,13 @@
+<html>
+<head>
+<style>
+ .a { width: 17895680}
+ .b { width: 10}
+</style>
+</head>
+<body>
+<table><tr>
+<td class="a">a</td><td class="b">b</td>
+</tr></table>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/410728-1.xml b/gfx/tests/crashtests/410728-1.xml
new file mode 100644
index 0000000000..deaeb8fc55
--- /dev/null
+++ b/gfx/tests/crashtests/410728-1.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0"?>
+<!DOCTYPE html PUBLIC
+ "-//W3C//DTD XHTML 1.1 plus MathML 2.0//EN"
+ "http://www.w3.org/TR/MathML2/dtd/xhtml-math11-f.dtd"
+[
+ <!ENTITY mathml "http://www.w3.org/1998/Math/MathML">
+]>
+<math display="block" xmlns="&mathml;">
+ <mi>f &#x0332;</mi>
+ <mi>-</mi>
+ <mo>+</mo>
+ <mo> -</mo>
+ <mo>&#x0332;</mo>
+</math>
diff --git a/gfx/tests/crashtests/416637-1.html b/gfx/tests/crashtests/416637-1.html
new file mode 100644
index 0000000000..48323a7448
--- /dev/null
+++ b/gfx/tests/crashtests/416637-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body style="font-size: 10000%">&#x2029;&#x0301;</body>
+</html>
diff --git a/gfx/tests/crashtests/419095-1.html b/gfx/tests/crashtests/419095-1.html
new file mode 100644
index 0000000000..9f47af2626
--- /dev/null
+++ b/gfx/tests/crashtests/419095-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0x202E)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0x000D)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0x200D)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0xD75A)));
+ document.body.appendChild(document.createTextNode(String.fromCharCode(0xD63F)));
+}
+
+</script>
+</head>
+
+<body onload="boom();">
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/419255-1.html b/gfx/tests/crashtests/419255-1.html
new file mode 100644
index 0000000000..acde950dd8
--- /dev/null
+++ b/gfx/tests/crashtests/419255-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body><div style="text-transform: capitalize">&#x5426; &#x200C;</body>
+</html>
diff --git a/gfx/tests/crashtests/420945-1.html b/gfx/tests/crashtests/420945-1.html
new file mode 100644
index 0000000000..a6eb2d57c7
--- /dev/null
+++ b/gfx/tests/crashtests/420945-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body dir="rtl"><div>&#x200C;&#x2028;</div></body>
+</html>
diff --git a/gfx/tests/crashtests/420962-1.html b/gfx/tests/crashtests/420962-1.html
new file mode 100644
index 0000000000..f8cf8b453d
--- /dev/null
+++ b/gfx/tests/crashtests/420962-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body><div>&#x0301;&#x2029;</div></body>
+</html>
diff --git a/gfx/tests/crashtests/421393-1.html b/gfx/tests/crashtests/421393-1.html
new file mode 100644
index 0000000000..82b6974675
--- /dev/null
+++ b/gfx/tests/crashtests/421393-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ document.body.appendChild(document.createTextNode("\uCEDD\u5C76\u000D\uCA34"));
+}
+
+</script>
+</head>
+
+<body onload="boom();"><span>&#x202E;&#x05BC;</span></body>
+</html>
diff --git a/gfx/tests/crashtests/421813-1.html b/gfx/tests/crashtests/421813-1.html
new file mode 100644
index 0000000000..885408b1b4
--- /dev/null
+++ b/gfx/tests/crashtests/421813-1.html
@@ -0,0 +1,4 @@
+<html>
+<head></head>
+<body onload="document.body.appendChild(document.createTextNode('y'));">x&#x2028;&#x200D;&#x202D;<span>&#x0643;</span></body>
+</html>
diff --git a/gfx/tests/crashtests/423110-1.xhtml b/gfx/tests/crashtests/423110-1.xhtml
new file mode 100644
index 0000000000..105638b0e8
--- /dev/null
+++ b/gfx/tests/crashtests/423110-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="min-width: max-content; float: left;"><head style="padding: 200%; display: -moz-inline-box; float: inherit;"></head></html>
diff --git a/gfx/tests/crashtests/423270-1.html b/gfx/tests/crashtests/423270-1.html
new file mode 100644
index 0000000000..e7c1d606b7
--- /dev/null
+++ b/gfx/tests/crashtests/423270-1.html
@@ -0,0 +1,5 @@
+<html>
+<body>
+<p style="font-family: DejaVu Sans">&#814;</p>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/428633.html b/gfx/tests/crashtests/428633.html
new file mode 100644
index 0000000000..cbf601a4a9
--- /dev/null
+++ b/gfx/tests/crashtests/428633.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body>A&#x101747;AAAAAAA&#x4f47;AAAAA&#xe33;AAAAAA</body>
+</html>
diff --git a/gfx/tests/crashtests/429899-1.html b/gfx/tests/crashtests/429899-1.html
new file mode 100644
index 0000000000..11cccd6862
--- /dev/null
+++ b/gfx/tests/crashtests/429899-1.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><body><span>&#x1104</span><span>&#x1104</span><span>&#x116A</span></body></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/441360.html b/gfx/tests/crashtests/441360.html
new file mode 100644
index 0000000000..0f06414e78
--- /dev/null
+++ b/gfx/tests/crashtests/441360.html
@@ -0,0 +1,39 @@
+<html><head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug </title>
+ <style type="text/css">
+
+ html,body {
+ color:black; background-color:white; font-size:16px; padding:0; margin:0;
+ }
+
+
+ </style>
+</head>
+<body>
+
+<div id="Image"></div>
+
+<script>
+var v;
+function insertImage() {
+ var img_node = document.createElement('iframe');
+ img_node.src = "441360_data.gif";
+ var image_div = document.getElementById('Image');
+ image_div.appendChild(img_node);
+}
+
+insertImage();
+v = document.body.offsetHeight;
+insertImage();
+v = document.body.offsetHeight;
+insertImage();
+v = document.body.offsetHeight;
+insertImage();
+v = document.body.offsetHeight;
+
+</script>
+
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/441360_data.gif b/gfx/tests/crashtests/441360_data.gif
new file mode 100644
index 0000000000..96532dc214
--- /dev/null
+++ b/gfx/tests/crashtests/441360_data.gif
Binary files differ
diff --git a/gfx/tests/crashtests/445711.html b/gfx/tests/crashtests/445711.html
new file mode 100644
index 0000000000..6ebd37eef2
--- /dev/null
+++ b/gfx/tests/crashtests/445711.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/REC-html401-19991224/strict.dtd">
+<html>
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <title>Tamil testcase</title>
+ </head>
+ <body>
+ <p>&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;&#x0BCC;</p>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/463307-1.html b/gfx/tests/crashtests/463307-1.html
new file mode 100644
index 0000000000..2d8eca3173
--- /dev/null
+++ b/gfx/tests/crashtests/463307-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html style="position: relative; bottom: 4449920388in; margin: 200px;">
+<head></head>
+<body style="background: url(../../../layout/reftests/bugs/repeatable-diagonal-gradient.png);"></body>
+</html>
diff --git a/gfx/tests/crashtests/467703-1.xhtml b/gfx/tests/crashtests/467703-1.xhtml
new file mode 100644
index 0000000000..e315baa327
--- /dev/null
+++ b/gfx/tests/crashtests/467703-1.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml" style="margin: 78504em; background: url(../../../testing/crashtest/images/tree.gif); font-size: 305203ch; position: relative; left: 65em;"><head></head><body></body></html>
diff --git a/gfx/tests/crashtests/467873-1.html b/gfx/tests/crashtests/467873-1.html
new file mode 100644
index 0000000000..ac4a6cdd09
--- /dev/null
+++ b/gfx/tests/crashtests/467873-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('q').appendChild(document.createTextNode('C'));">
+<div style="white-space: pre; direction: rtl;">A<div id="q" style="text-transform: capitalize;">B
+<div></div></div></div>
+</body></html>
diff --git a/gfx/tests/crashtests/470418-1.html b/gfx/tests/crashtests/470418-1.html
new file mode 100644
index 0000000000..7f13b87357
--- /dev/null
+++ b/gfx/tests/crashtests/470418-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE html>
+<html>
+<head></head>
+<body><div style="text-transform: capitalize">&#x06CD;A</div></body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/474410-1.html b/gfx/tests/crashtests/474410-1.html
new file mode 100644
index 0000000000..c7ea8e7b24
--- /dev/null
+++ b/gfx/tests/crashtests/474410-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style type="text/css">
+
+div { -moz-transform: matrix(3, 1, 16.8, 5.6, 0, 0); }
+div > div { border: 1px dashed #9ab; }
+
+</style>
+</head>
+<body>
+
+<div><div></div></div>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/487549-1.html b/gfx/tests/crashtests/487549-1.html
new file mode 100644
index 0000000000..c20cb483f7
--- /dev/null
+++ b/gfx/tests/crashtests/487549-1.html
@@ -0,0 +1,23 @@
+<html>
+<head>
+<title>Bad kern table - bug 487549</title>
+<!--
+ The font used here has an invalid 'kern' table that will crash ATSUI
+ if we attempt to use it.
+ See https://bugzilla.mozilla.org/show_bug.cgi?id=487549
+-->
+<style>
+@font-face {
+ font-family: bad-kern-font;
+ src: url(487549-bad_kern_table.ttf) format("truetype");
+}
+
+body {
+ font-family: bad-kern-font;
+}
+</style>
+</head>
+<body>
+ABC abc 123
+</body>
+</html>
diff --git a/gfx/tests/crashtests/487549-bad_kern_table.ttf b/gfx/tests/crashtests/487549-bad_kern_table.ttf
new file mode 100644
index 0000000000..d8da04a3af
--- /dev/null
+++ b/gfx/tests/crashtests/487549-bad_kern_table.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/487724-1.html b/gfx/tests/crashtests/487724-1.html
new file mode 100644
index 0000000000..abc158b43e
--- /dev/null
+++ b/gfx/tests/crashtests/487724-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script type="text/javascript">
+
+function boom()
+{
+ var w = document.getElementById("w");
+ var q = document.getElementById("q");
+ var e = document.createTextNode("");
+ document.documentElement.appendChild(document.body);
+ w.appendChild(e);
+ document.documentElement.offsetHeight;
+ w.removeChild(q);
+ document.documentElement.offsetHeight;
+ e.data += " x ";
+}
+
+</script>
+</head>
+
+<body onload="boom();"><span id="w">s&#x202E;<span id="q"></span><div></div><span>a</span></span></body>
+</html>
diff --git a/gfx/tests/crashtests/490777-1.html b/gfx/tests/crashtests/490777-1.html
new file mode 100644
index 0000000000..f7e42b54b7
--- /dev/null
+++ b/gfx/tests/crashtests/490777-1.html
@@ -0,0 +1,9 @@
+<!-- This crashed on Mac OS X with the modified ATSUI font backend implemented in
+ bug 481948. Crash occurs due to an unpaired low surrogate in text with the
+ right-to-left direction override; this cannot occur in direct HTML content
+ because the unpaired surrogate will be replaced with U+FFFD, but it can be
+ generated from Javascript. -->
+<html>
+<body onload="document.body.appendChild(document.createTextNode('\u202E\u4839\uDC1D'));">
+</body>
+</html>
diff --git a/gfx/tests/crashtests/516512-1.html b/gfx/tests/crashtests/516512-1.html
new file mode 100644
index 0000000000..028e4507a1
--- /dev/null
+++ b/gfx/tests/crashtests/516512-1.html
@@ -0,0 +1,5 @@
+<!DOCTYPE HTML>
+<html style="font-size: 70368744177663%; -moz-background-size: 2199023255552em 809464690865px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR42mP4%2F58BAAT%2FAf9jgNErAAAAAElFTkSuQmCC);">
+<body>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/532726-1.html b/gfx/tests/crashtests/532726-1.html
new file mode 100644
index 0000000000..d89a52475e
--- /dev/null
+++ b/gfx/tests/crashtests/532726-1.html
@@ -0,0 +1,5 @@
+<html>
+<body style="word-spacing: 10px">
+<div>X &#x0301;&#x0000;</div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/538065-1.html b/gfx/tests/crashtests/538065-1.html
new file mode 100644
index 0000000000..ff6669beef
--- /dev/null
+++ b/gfx/tests/crashtests/538065-1.html
@@ -0,0 +1,14 @@
+<html>
+<head>
+<title>Testcase for bug 538065</title>
+<style type="text/css">
+span.test { background: #ff0; }
+</style>
+</head>
+<body>
+<p>U+FEFF: <span class="test">&#xfeff;</span></p>
+<p>U+FFF9: <span class="test">&#xfff9;</span></p>
+<p>U+FFFA: <span class="test">&#xfffa;</span></p>
+<p>U+FFFB: <span class="test">&#xfffb;</span></p>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/546870-1.html b/gfx/tests/crashtests/546870-1.html
new file mode 100644
index 0000000000..bc83d90e05
--- /dev/null
+++ b/gfx/tests/crashtests/546870-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body onload="document.getElementById('button').value='';">
+<input id="button" type="button" value="Should not crash" style="width: 1px; white-space: normal;">
+</body>
+</html>
diff --git a/gfx/tests/crashtests/557348-1.html b/gfx/tests/crashtests/557348-1.html
new file mode 100644
index 0000000000..828939cfd6
--- /dev/null
+++ b/gfx/tests/crashtests/557348-1.html
@@ -0,0 +1 @@
+<html style="background: repeating-radial-gradient(circle closest-side at left center, red, white 100px, black); width: 300px; height: 1px;"></html>
diff --git a/gfx/tests/crashtests/563740-1.html b/gfx/tests/crashtests/563740-1.html
new file mode 100644
index 0000000000..8873cfa3ad
--- /dev/null
+++ b/gfx/tests/crashtests/563740-1.html
@@ -0,0 +1,2 @@
+<html><body style="font-size-adjust: -18446744073709552000; font-weight: bold;">&#x9385;</body></html>
+
diff --git a/gfx/tests/crashtests/580100-1.html b/gfx/tests/crashtests/580100-1.html
new file mode 100644
index 0000000000..41e4a474f3
--- /dev/null
+++ b/gfx/tests/crashtests/580100-1.html
@@ -0,0 +1,7 @@
+<html><head>
+<style>@font-face{font-family:t;src:url(580100-bad_hhea_table.ttf);}
+p.t{font-size:40px;font-family:t;}</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p></body>
+</html>
diff --git a/gfx/tests/crashtests/580100-bad_hhea_table.ttf b/gfx/tests/crashtests/580100-bad_hhea_table.ttf
new file mode 100644
index 0000000000..229767423c
--- /dev/null
+++ b/gfx/tests/crashtests/580100-bad_hhea_table.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/580212-1.html b/gfx/tests/crashtests/580212-1.html
new file mode 100644
index 0000000000..b5af08ddc4
--- /dev/null
+++ b/gfx/tests/crashtests/580212-1.html
@@ -0,0 +1,7 @@
+<html><head>
+<style>@font-face{font-family:t;src:url(580212-bad_loca_table.ttf);}
+p.t{font-size:40px;font-family:t;}</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p></body>
+</html>
diff --git a/gfx/tests/crashtests/580212-bad_loca_table.ttf b/gfx/tests/crashtests/580212-bad_loca_table.ttf
new file mode 100644
index 0000000000..ec303a922c
--- /dev/null
+++ b/gfx/tests/crashtests/580212-bad_loca_table.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/580233-1.html b/gfx/tests/crashtests/580233-1.html
new file mode 100644
index 0000000000..07d4abdcb2
--- /dev/null
+++ b/gfx/tests/crashtests/580233-1.html
@@ -0,0 +1,7 @@
+<html><head>
+<style>@font-face{font-family:t;src:url(580233-bad_gpos_table.ttf);}
+p.t{font-size:40px;font-family:t;}</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p></body>
+</html>
diff --git a/gfx/tests/crashtests/580233-bad_gpos_table.ttf b/gfx/tests/crashtests/580233-bad_gpos_table.ttf
new file mode 100644
index 0000000000..c753040ca3
--- /dev/null
+++ b/gfx/tests/crashtests/580233-bad_gpos_table.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/580719-1.html b/gfx/tests/crashtests/580719-1.html
new file mode 100644
index 0000000000..29fe9f73d3
--- /dev/null
+++ b/gfx/tests/crashtests/580719-1.html
@@ -0,0 +1,18 @@
+<html>
+<head>
+<style>
+@font-face {
+ font-family: t;
+ src: url(580719-bad_head_table.ttf);
+}
+
+p.t {
+ font-size: 40px;
+ font-family: t;
+}
+</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/580719-bad_head_table.ttf b/gfx/tests/crashtests/580719-bad_head_table.ttf
new file mode 100644
index 0000000000..9ad4896b02
--- /dev/null
+++ b/gfx/tests/crashtests/580719-bad_head_table.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/593526.html b/gfx/tests/crashtests/593526.html
new file mode 100644
index 0000000000..f0de065dcc
--- /dev/null
+++ b/gfx/tests/crashtests/593526.html
@@ -0,0 +1 @@
+<html style="max-width: 51079px; width: 730.549in; -moz-appearance: checkbox; box-shadow: 0.2em 0.2em rgb(204, 204, 204);">
diff --git a/gfx/tests/crashtests/593526.xhtml b/gfx/tests/crashtests/593526.xhtml
new file mode 100644
index 0000000000..d1da4c3d81
--- /dev/null
+++ b/gfx/tests/crashtests/593526.xhtml
@@ -0,0 +1,5 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<box style="max-width: 51079px; width: 730.549in; -moz-appearance: checkbox; box-shadow: 0.2em 0.2em rgb(204, 204, 204);"></box>
+</window>
+
diff --git a/gfx/tests/crashtests/594654-1.xhtml b/gfx/tests/crashtests/594654-1.xhtml
new file mode 100644
index 0000000000..4ab2ce6e7d
--- /dev/null
+++ b/gfx/tests/crashtests/594654-1.xhtml
@@ -0,0 +1,5 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body onload="document.getElementById('x').appendChild(document.createElementNS('http://www.w3.org/1998/Math/MathML', 'mrow'));">
+<div style="position: fixed;"><div><msubsup xmlns="http://www.w3.org/1998/Math/MathML" id="x"/></div><menclose xmlns="http://www.w3.org/1998/Math/MathML"/></div><mroot xmlns="http://www.w3.org/1998/Math/MathML"/>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/595042-1.html b/gfx/tests/crashtests/595042-1.html
new file mode 100644
index 0000000000..0cc2b22477
--- /dev/null
+++ b/gfx/tests/crashtests/595042-1.html
@@ -0,0 +1 @@
+<html style="-moz-box-shadow: 0 0 0.2em blue; -moz-appearance: button;"><body style="padding: 113in;"></body></html>
diff --git a/gfx/tests/crashtests/595727-1.html b/gfx/tests/crashtests/595727-1.html
new file mode 100644
index 0000000000..d2012c7153
--- /dev/null
+++ b/gfx/tests/crashtests/595727-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<script>
+
+function doTest()
+{
+ var r = document.documentElement;
+ while(r.firstChild) { r.firstChild.remove(); }
+ var body = document.createElementNS("http://www.w3.org/1999/xhtml", "body");
+ r.appendChild(body);
+ body.contentEditable = "true";
+ document.execCommand("inserthtml", false, "<span style=\"position:relative;left:0.8px\">a<select></select>a</span>");
+
+ document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doTest);
+</script>
+</head>
+
+<body></body>
+</html>
diff --git a/gfx/tests/crashtests/624198.xhtml b/gfx/tests/crashtests/624198.xhtml
new file mode 100644
index 0000000000..67d207b7a1
--- /dev/null
+++ b/gfx/tests/crashtests/624198.xhtml
@@ -0,0 +1 @@
+<html xmlns="http://www.w3.org/1999/xhtml"><msqrt xmlns="http://www.w3.org/1998/Math/MathML"><mpadded depth="+98774970791px"/></msqrt></html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/633322-1.html b/gfx/tests/crashtests/633322-1.html
new file mode 100644
index 0000000000..89b0aaa612
--- /dev/null
+++ b/gfx/tests/crashtests/633322-1.html
@@ -0,0 +1 @@
+<!DOCTYPE html><html><body><div style="text-transform: uppercase">&#xA6F8;&#xDF;&#x200B;</div></body></html>
diff --git a/gfx/tests/crashtests/633453-1.html b/gfx/tests/crashtests/633453-1.html
new file mode 100644
index 0000000000..2b08776c09
--- /dev/null
+++ b/gfx/tests/crashtests/633453-1.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+</head>
+<body>
+<p>Откуда: אֶרֶץ יִשְׂרָאֵל‎</p>
+</body>
+</html>
+
diff --git a/gfx/tests/crashtests/662467-1.html b/gfx/tests/crashtests/662467-1.html
new file mode 100644
index 0000000000..ccf3c7d8ec
--- /dev/null
+++ b/gfx/tests/crashtests/662467-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html><html style="min-height: 1540095em; opacity: 0.2; -moz-appearance: toolbar"><body></body></html>
+
diff --git a/gfx/tests/crashtests/665218.html b/gfx/tests/crashtests/665218.html
new file mode 100644
index 0000000000..909ca941df
--- /dev/null
+++ b/gfx/tests/crashtests/665218.html
@@ -0,0 +1,8 @@
+<html class="reftest-paged"><head>
+<style id="e"> @font-face {
+ font-family: "aaa";
+ src: url("doesnotexist.TTF");
+ }
+ * { font-family: "aaa"; }</style>
+</head>
+<body>
diff --git a/gfx/tests/crashtests/675550-1.html b/gfx/tests/crashtests/675550-1.html
new file mode 100644
index 0000000000..d6c25b59e0
--- /dev/null
+++ b/gfx/tests/crashtests/675550-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+
+.justify {
+ width: 400px;
+ text-align: justify;
+ background: lightgreen;
+}
+
+.force {
+ width: 400px;
+ display: inline-block;
+ height: 3px;
+ background: yellow;
+}
+
+</style>
+</head>
+<body onload="document.getElementById('s').textContent='\u202E\0 \u1DCEz'; /* cannot express \0 in html markup using amp escape */">
+<div class="justify"><span class="force"></span><span id="s"></span><span class="force"></span></div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/686190-1.html b/gfx/tests/crashtests/686190-1.html
new file mode 100644
index 0000000000..26cda094fa
--- /dev/null
+++ b/gfx/tests/crashtests/686190-1.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<head>
+<style type="text/css">
+@font-face {
+ font-family: foo;
+ src: url(Prototype.ttf);
+}
+body {
+ font-family: foo;
+ font-size: 2000px;
+ font-weight: 900;
+}
+</style>
+</head>
+<body>
+xyzzy
+</body>
+</html>
diff --git a/gfx/tests/crashtests/691330.svg b/gfx/tests/crashtests/691330.svg
new file mode 100644
index 0000000000..121fe22702
--- /dev/null
+++ b/gfx/tests/crashtests/691330.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg"><text rotate="0" stroke="black">a&#x00AD;</text></svg>
diff --git a/gfx/tests/crashtests/691581-1.html b/gfx/tests/crashtests/691581-1.html
new file mode 100644
index 0000000000..ce2f35eaf7
--- /dev/null
+++ b/gfx/tests/crashtests/691581-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="font-size: 0.03rem;">
+<body>
+ Test Text
+</body>
+</html>
diff --git a/gfx/tests/crashtests/693143-1.html b/gfx/tests/crashtests/693143-1.html
new file mode 100644
index 0000000000..80b1e891ac
--- /dev/null
+++ b/gfx/tests/crashtests/693143-1.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Tiny ugly fonts</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+
+<style type="text/css">
+
+body {
+ margin: 50px;
+}
+
+p, div {
+ margin: 0;
+}
+
+#test {
+ font-size: 1px;
+}
+
+#test p {
+ font-size: 8.3%;
+}
+
+#f1 { font-family: Terminal; }
+#f2 { font-family: FixedSys; }
+#f3 { font-family: Script; }
+#f4 { font-family: Roman; }
+
+</style>
+
+</head>
+<body>
+
+<h4>No text should show below this line</h4>
+<div id="test">
+<p id="f1">ugly font</p>
+<p id="f2">ugly font</p>
+<p id="f3">ugly font</p>
+<p id="f4">ugly font</p>
+</div>
+
+</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/696936-1.html b/gfx/tests/crashtests/696936-1.html
new file mode 100644
index 0000000000..0ce95ddd6f
--- /dev/null
+++ b/gfx/tests/crashtests/696936-1.html
@@ -0,0 +1,2 @@
+<!-- quirks mode only -->
+<body style="-moz-transform: perspective(-3);"></body>
diff --git a/gfx/tests/crashtests/699563-1.html b/gfx/tests/crashtests/699563-1.html
new file mode 100644
index 0000000000..e73968d714
--- /dev/null
+++ b/gfx/tests/crashtests/699563-1.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html><html style="width: 16px; height: 16px; -moz-transform: matrix3d(1, 2, 300, 4, 5, 6000, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); filter: url(&quot;#none&quot;);"><div style="height: 100px; width: 100px;"></div></html>
+
diff --git a/gfx/tests/crashtests/710149-1.html b/gfx/tests/crashtests/710149-1.html
new file mode 100644
index 0000000000..930ef95512
--- /dev/null
+++ b/gfx/tests/crashtests/710149-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+
+<script>
+
+function boom()
+{
+ var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ d.style.setProperty("-moz-transform", "translate(0pt, 10px)", "");
+ d.style.setProperty("opacity", "0.8", "");
+ d.style.setProperty("background-color", "gray", "");
+ var c = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ (d).appendChild(c);
+ (document.body).appendChild(d);
+ c.getContext("2d");
+}
+
+</script>
+
+<body onload="setTimeout(boom, 100);"></body>
diff --git a/gfx/tests/crashtests/746491.html b/gfx/tests/crashtests/746491.html
new file mode 100644
index 0000000000..6605fbd1ed
--- /dev/null
+++ b/gfx/tests/crashtests/746491.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.lineTo(0, 3);
+ ctx.moveTo(0, 1);
+ ctx.isPointInPath(0, 2);
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/746495.html b/gfx/tests/crashtests/746495.html
new file mode 100644
index 0000000000..d3a6f943b2
--- /dev/null
+++ b/gfx/tests/crashtests/746495.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.scale(69.25, 61);
+ ctx.transform(87.875, 89.5, 317.6666666666667, 86.5, 93, 196);
+ ctx.bezierCurveTo(30.142857142857142, 13926147670, 66.66666666666667, 243.66666666666666, 42.6, 85.5);
+ ctx.arc(1.6, 364955956612165500, 110.375, 15.1, 80.55555555555556, 0);
+ ctx.shadowColor = "green";
+ ctx.shadowOffsetY = 754;
+ ctx.lineJoin = 'round';
+ ctx.setTransform(95.125, 11, 834, 34.5, 265.3333333333333, 26.4);
+ ctx.stroke();
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/746497.html b/gfx/tests/crashtests/746497.html
new file mode 100644
index 0000000000..5e84445b4a
--- /dev/null
+++ b/gfx/tests/crashtests/746497.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.arc(112.75, 22.8, 10.142857142857142, 20.5, 196, 1);
+ ctx.translate(48.22222222222222, 18.25);
+ ctx.lineTo(7, 50);
+ ctx.scale(445, 24.22222222222222);
+ ctx.stroke();
+ ctx.stroke();
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/746844.html b/gfx/tests/crashtests/746844.html
new file mode 100644
index 0000000000..24e7b59acb
--- /dev/null
+++ b/gfx/tests/crashtests/746844.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.rotate(108);
+ ctx.shadowBlur = 220.75;
+ ctx.shadowColor = "#777777";
+ ctx.bezierCurveTo(450, 24.857142857142858, 191, 75.71428571428571, -1125899906842623, 0);
+ ctx.transform(843, 152.66666666666666, 42.7, 65.33333333333333, 86.66666666666667, 30.714285714285715);
+ ctx.transform(204, 112.5, 159, 96.75, 239.33333333333334, 0);
+ ctx.bezierCurveTo(42.4, 96.44444444444444, 480.5, 185.5, 44.666666666666664, 90.14285714285714);
+ ctx.isPointInPath(71.5, 81);
+ ctx.fill();
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/746847.html b/gfx/tests/crashtests/746847.html
new file mode 100644
index 0000000000..01853a4030
--- /dev/null
+++ b/gfx/tests/crashtests/746847.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.setTransform(75.875, 113.8, 144, 55, 185.4, 310);
+ ctx.translate(26.5, 72.6);
+ ctx.arcTo(183.6, 29.6, 94.33333333333333, 1, 72057594037927940);
+ ctx.arcTo(75.83333333333333, 73.71428571428571, 40.4, 88.8, 23.285714285714285);
+ ctx.fill();
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/746849.html b/gfx/tests/crashtests/746849.html
new file mode 100644
index 0000000000..0544564706
--- /dev/null
+++ b/gfx/tests/crashtests/746849.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.rect(1, 2, 3, 4);
+ ctx.fill();
+ for (var i = 0; i < 48; ++i) {
+ ctx.scale(100, 100);
+ }
+ ctx.isPointInPath(5, 6);
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/746866.html b/gfx/tests/crashtests/746866.html
new file mode 100644
index 0000000000..c61d21b174
--- /dev/null
+++ b/gfx/tests/crashtests/746866.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.rotate(17);
+ ctx.closePath();
+ ctx.setTransform(107, 16, 113, 76, 0, 165);
+ ctx.lineTo(4, 0);
+ ctx.arc(104, -8191, 35, 50, 359, 0);
+ ctx.scale(74.75, 729);
+ ctx.stroke();
+ ctx.stroke();
+ ctx.lineTo(-1, 40);
+ ctx.isPointInPath(92, 463);
+ ctx.clip();
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/747132.html b/gfx/tests/crashtests/747132.html
new file mode 100644
index 0000000000..e4405ada07
--- /dev/null
+++ b/gfx/tests/crashtests/747132.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.strokeRect(0.5, 1, 2, 3);
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="100" height="100"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/747302.html b/gfx/tests/crashtests/747302.html
new file mode 100644
index 0000000000..7b202a7d5b
--- /dev/null
+++ b/gfx/tests/crashtests/747302.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.querySelector("canvas").getContext('2d');
+ ctx.rotate(470);
+ ctx.clearRect(71.5, -0.5, 666, 380);
+}
+
+</script>
+</head>
+<body onload="boom();"><canvas width="41700" height="500"></canvas></body>
+</html>
diff --git a/gfx/tests/crashtests/766422-1.html b/gfx/tests/crashtests/766422-1.html
new file mode 100644
index 0000000000..887df3b1d8
--- /dev/null
+++ b/gfx/tests/crashtests/766422-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html style="-moz-perspective: 1000px; -moz-transform: skewY(277deg); overflow: clip;">
+ <body style="-moz-transform: skewY(127deg);">
+ <div style="height: 20px; background: lightgreen;"></div>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/766422-2.html b/gfx/tests/crashtests/766422-2.html
new file mode 100644
index 0000000000..fe4819354a
--- /dev/null
+++ b/gfx/tests/crashtests/766422-2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+
+function boom()
+{
+ var ctx = document.getElementById("c").getContext('2d');
+ ctx.stroke();
+}
+
+</script>
+</head>
+<body onload="boom();">
+<canvas id="c" width="800" style="overflow: clip; transform: skewY(30rad);"></canvas>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/766452-1.html b/gfx/tests/crashtests/766452-1.html
new file mode 100644
index 0000000000..abb6969796
--- /dev/null
+++ b/gfx/tests/crashtests/766452-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<script>
+
+document.createElement('canvas').getContext('2d').measureText("\u0CC4\u0CA7\u200C");
+
+</script>
diff --git a/gfx/tests/crashtests/766452-2.html b/gfx/tests/crashtests/766452-2.html
new file mode 100644
index 0000000000..b70d6f8137
--- /dev/null
+++ b/gfx/tests/crashtests/766452-2.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+<body>
+<div style="font-family:Arial Unicode MS">&#x0CC4;&#x0CA7;&#x200C;</div>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/768079-1.html b/gfx/tests/crashtests/768079-1.html
new file mode 100644
index 0000000000..8a89d1ed13
--- /dev/null
+++ b/gfx/tests/crashtests/768079-1.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html style="-moz-perspective: 7000px; overflow: hidden;">
+<body style="overflow: hidden; -moz-transform: rotateX(30deg); border-radius: 1px; columns: 2 10px; visibility: collapse;">X</body>
+</html>
diff --git a/gfx/tests/crashtests/783041-1.html b/gfx/tests/crashtests/783041-1.html
new file mode 100644
index 0000000000..0e57a73156
--- /dev/null
+++ b/gfx/tests/crashtests/783041-1.html
@@ -0,0 +1,63 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el7 {
+ font-size:.92em
+ }
+ #el0 {
+ height: 200px ! important;
+ margin: 0px;
+ display: table;
+ font-size:.92em
+ }
+
+ #el3 {
+ line-height: 0.5px;
+ text-shadow: 0px 5px 5px, 0px -20px 10px;
+ display: table-row-group;
+ transform: translate3d(-3px, -300px, 0px);
+ }
+
+ #el5 {
+ height:1em;
+ display:block;
+ }
+
+ .c4 {
+ margin: 1em;
+ padding: 0.5em;
+ }
+ </style>
+ <script>
+ onload = function() {
+ el7=document.createElement('iframe')
+ el7.setAttribute('id', 'el7')
+ document.body.appendChild(el7)
+
+ el0=document.createElement('span')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+ el0.appendChild(document.createTextNode('A'))
+
+ el3=document.createElement('q')
+ el3.setAttribute('id','el3')
+ el0.appendChild(el3)
+
+ el5=document.createElement('q')
+ el5.setAttribute('id','el5')
+ el3.appendChild(el5)
+
+ el0.appendChild(document.createTextNode('A'))
+
+ document.body.offsetTop
+ el0.setAttribute('class', 'c4');
+ el7.setAttribute('class', 'c4');
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/783041-2.html b/gfx/tests/crashtests/783041-2.html
new file mode 100644
index 0000000000..8753bd9724
--- /dev/null
+++ b/gfx/tests/crashtests/783041-2.html
@@ -0,0 +1,73 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el0 {
+ height: 200px ! important;
+ height: 1em;
+ width: 1em;
+ padding: 5px;
+ display: table;
+ -moz-transform:translate3d(0, 80px, 0);
+ }
+ #el0:before {
+ display: -moz-box;
+ content: counter(c, hiragana) attr(id);
+ counter-increment: c 694;
+ }
+ #el0:after {
+ counter-reset: c 694;
+ content: counter(c, cjk-ideographic) attr(id);
+ }
+ #el1 {
+ text-shadow: 0px 20px 0px, 0px -20px 10px;
+ line-height: 4px;
+ transform: translate3d(0px, -300px, 0px);
+ display: table-row-group;
+ border-spacing: 7px;
+ }
+ #el1:after {
+ counter-reset: c;
+ display: -moz-box;
+ content: counter(c, cjk-ideographic) attr(id);
+ counter-increment: c 694;
+ }
+ #el2 {
+ display: table-row-group;
+ -moz-transform:translate3d(0, 80px, 0);
+ }
+ #el2:after {
+ content: counter(c, cjk-ideographic) attr(id);
+ }
+ </style>
+ <script>
+ function MaybeReload() {
+ var countdown = 10;
+ if (location.search) {
+ countdown = parseInt(location.search.slice(1)) - 1;
+ }
+ if (countdown > 0) {
+ location.search = countdown;
+ }
+ }
+
+ onload = function() {
+ el0=document.createElement('div')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+ el1=document.createElement('div')
+ el1.setAttribute('id','el1')
+ el0.appendChild(el1)
+ el2=document.createElement('q')
+ el2.setAttribute('id','el2')
+ el1.appendChild(el2)
+ el0.appendChild(document.createTextNode('A'))
+ setTimeout("MaybeReload()", 100)
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/783041-3.html b/gfx/tests/crashtests/783041-3.html
new file mode 100644
index 0000000000..efdbced5ec
--- /dev/null
+++ b/gfx/tests/crashtests/783041-3.html
@@ -0,0 +1,71 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el0 {
+ height: 200px ! important;
+ padding: 4px;
+ white-space: pre-wrap;
+ margin-top: 0px;
+ display: table;
+ }
+ #el3 {
+ line-height: 3px;
+ text-shadow: 0px 5px 5px, 0px -20px 10px;
+ }
+ #el3:before { display: inline-block; }
+ #el3:first-of-type { transform: translate3d(-30px, -300px, 0px); }
+ #el3:first-child { display: table-row-group; }
+ #el5:before { -moz-margin-before:1em; display: list-item; content: counter(c, ethiopic-halehame-tig) attr(id); counter-increment: c 810; }
+ #el5 { list-style-type:lower-greek }
+ #el5 { background:red;margin:0;height:1em;display:block }
+ #el5:nth-child(3) { -moz-svg-shadow:5px 5px 5px red; display: link; content: counter(c, asterisks) attr(id); counter-increment: c 266; }
+
+ .c4 {
+ margin: 1em; }
+ #el7 { font-size:.92em }
+ #el3 { font-size:.92em }
+ .c4 { padding:0.4em 0.5em 0.4em 2.5em }
+ .c4:after { margin:2px; display: -moz-box; content: counter(c, octal) attr(id); counter-increment: c 171; }
+ .c4:before { -moz-user-drag:none; display: -moz-inline-flexbox; content: counter(c, hiragana-iroha) attr(id); counter-increment: c 209; }
+ .c4:only-of-type { -moz-logical-height:50px; display: -moz-box; content: counter(c, binary) attr(id); counter-increment: c 871; }
+ </style>
+ <script>
+ onload = function() {
+ el0=document.createElement('ul')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+
+ el1=document.createElement('canvas')
+ document.body.appendChild(el1)
+
+
+ el3=document.createElement('q')
+ el3.setAttribute('id','el3')
+ el0.appendChild(el3)
+
+ el4=document.createElement('progress')
+ el4.setAttribute('id','el4')
+ document.body.appendChild(el4)
+
+ el5=document.createElement('q')
+ el5.setAttribute('id','el5')
+ el3.appendChild(el5)
+
+ el0.appendChild(document.createTextNode('A'))
+
+ el7=document.createElement('iframe')
+ el7.setAttribute('id', 'el7')
+ el0.parentNode.insertBefore(el7, el0)
+ el0.setAttribute('class', 'c4');
+ el3.setAttribute('class', 'c4');
+ el7.setAttribute('class', 'c4');
+ setTimeout("window.close()", 5000)
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/783041-4.html b/gfx/tests/crashtests/783041-4.html
new file mode 100644
index 0000000000..1538bfdf08
--- /dev/null
+++ b/gfx/tests/crashtests/783041-4.html
@@ -0,0 +1,82 @@
+<html>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+ <head>
+ <style>
+ #el0 {
+ height: 200px ! important;
+ padding: 4px;
+ white-space: pre-wrap;
+ margin-top: 0px;
+ display: table;
+ font-size:.92em
+ }
+
+ #el0:before {
+ content: counter(c, hiragana-iroha);
+ }
+
+ #el3 {
+ line-height: 3px;
+ text-shadow: 0px 5px 5px, 0px -20px 10px;
+ display: table-row-group;
+ transform: translate3d(-30px, -300px, 0px);
+ }
+
+ #el3:before {
+ display: inline-block;
+ }
+
+ #el5 {
+ height:1em;
+ display:block;
+ }
+
+ #el5:before {
+ display: list-item;
+ }
+
+ #el7 {
+ font-size:.92em
+ }
+ .c4 {
+ margin: 1em;
+ padding:0.4em 0.5em 0.4em 2.5em;
+ counter-increment: c;
+ }
+
+ .c4:after {
+ margin: 2px;
+ display: inline-block;
+ }
+ </style>
+ <script>
+ onload = function() {
+ el7=document.createElement('iframe')
+ el7.setAttribute('id', 'el7')
+ document.body.appendChild(el7)
+
+ el0=document.createElement('ul')
+ el0.setAttribute('id','el0')
+ document.body.appendChild(el0)
+
+
+ el3=document.createElement('q')
+ el3.setAttribute('id','el3')
+ el0.appendChild(el3)
+
+ el5=document.createElement('q')
+ el5.setAttribute('id','el5')
+ el3.appendChild(el5)
+
+ el0.appendChild(document.createTextNode('A'))
+
+ el0.setAttribute('class', 'c4');
+ el7.setAttribute('class', 'c4');
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/gfx/tests/crashtests/798853.html b/gfx/tests/crashtests/798853.html
new file mode 100644
index 0000000000..a0b16f77cd
--- /dev/null
+++ b/gfx/tests/crashtests/798853.html
@@ -0,0 +1,3 @@
+<p style="font-size-adjust: 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999; "><style>
+@-moz-keyframes cfpulse1 { 0% { opacity: 0.7301; font-size-adjust: 0.7684; } }
+* { -moz-animation-name: cfpulse1; -moz-animation-duration: 3s; \ No newline at end of file
diff --git a/gfx/tests/crashtests/805760-1.html b/gfx/tests/crashtests/805760-1.html
new file mode 100644
index 0000000000..9f89d96e73
--- /dev/null
+++ b/gfx/tests/crashtests/805760-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+<head>
+<style>
+@font-face{
+ font-family:t; src:url(805760.ttf);
+}
+p.t{
+ font-size:40px;
+ font-family:t;
+}
+span.t {
+ font-size:10px;
+ font-family:t;
+}
+</style>
+</head>
+<body>
+<p class="t">Lorem ipsum</p>
+<span class="t">Lorem ipsum</span>
+</body></html>
+
diff --git a/gfx/tests/crashtests/805760.ttf b/gfx/tests/crashtests/805760.ttf
new file mode 100644
index 0000000000..e02a769559
--- /dev/null
+++ b/gfx/tests/crashtests/805760.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/812826.html b/gfx/tests/crashtests/812826.html
new file mode 100644
index 0000000000..432536a4f3
--- /dev/null
+++ b/gfx/tests/crashtests/812826.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+<input value="&#x202E;Z&#x0301;&#x0301;">
+</body>
diff --git a/gfx/tests/crashtests/815489.html b/gfx/tests/crashtests/815489.html
new file mode 100644
index 0000000000..55053959aa
--- /dev/null
+++ b/gfx/tests/crashtests/815489.html
@@ -0,0 +1,17 @@
+><textarea></textarea><audio id=test1 style="cue-after: none; margin: 8508em -189em 182; ">><style>
+* { azimuth: behind; -moz-transform: skewy(86deg); }
+@-moz-keyframes cfpulse0 { 0% { opacity: 0.3175; transform: rotatex(67166.5665591deg); box-shadow: 167px 0px 8px ivory; quotes: "" "‹" "›"; } }
+* { -moz-animation-name: cfpulse0; -moz-animation-duration: 5s; background-color: -moz-buttonhoverface;</style><script>
+var docElement = document.body ? document.body : document.documentElement;
+function initCF() {
+try { test2 = document.createElementNS("http://www.w3.org/1999/xhtml", "tbody"); } catch(e) {}
+try { docElement.appendChild(test2); } catch(e) {}
+setTimeout("CFcrash()", 192);
+}
+document.addEventListener("DOMContentLoaded", initCF);
+function CFcrash() {
+try { test1.style.display = "table-column" } catch(e) {}
+setTimeout('try { var x = new XSLTProcessor(); x.transformToDocument(test1); } catch(e) {}', 205);
+setTimeout('try { docElement.appendChild(test1); } catch(e) {}', 391);
+try { test2.classList.add("not-exist"); } catch(e) {}
+}</script>> \ No newline at end of file
diff --git a/gfx/tests/crashtests/836225-1.html b/gfx/tests/crashtests/836225-1.html
new file mode 100644
index 0000000000..18f023f7c8
--- /dev/null
+++ b/gfx/tests/crashtests/836225-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<style type="text/css">
+@font-face {
+ font-family: foo;
+ src: url(PigLatin_Plane15.ttf);
+}
+body {
+ font-family: foo;
+ font-size: 24px;
+}
+</style>
+</head>
+<body>
+&#xf0050;&#xf0069;&#xf0067; &#xf004c;&#xf0061;&#xf0074;&#xf0069;&#xf006e;
+</body>
+</html>
diff --git a/gfx/tests/crashtests/839745-1.html b/gfx/tests/crashtests/839745-1.html
new file mode 100644
index 0000000000..f45b2f41d1
--- /dev/null
+++ b/gfx/tests/crashtests/839745-1.html
@@ -0,0 +1,20 @@
+<html>>><svg width="38.500000cm"> fill="#740000" height="64787">><rect height="182px" stroke="gray" width="39904px"></rect>
+<text font-size="52em" x="145 15 162 245 31261 251 143 3 2556045634 67 30 164 16925">k20n01 (21.00)</text>
+><rect height="44" stroke="gray" width="482.6"></rect>
+<text font-size="0.941166">3V97 T*2W t
+fSs
+_9 oCFRVeS
+@
+[^2h A3Y
+4
+ u
+vd`
+8%&amp;./h}*y|Z`6&amp;7 2?D4PoB|
+~#6b|/ak;sf?MaHHLAck Aee6fI*pU_i}5N%q? Qk7 uBJ l4;x7LlsrDu~:U=+P
+ *e#{z
+
+) n|NRXS:N
+ J p
+ZFfSc!W %rTL)#D+6Cd}0$
+k20n03 (22.00)</text>
+><text font-size="102em">> \ No newline at end of file
diff --git a/gfx/tests/crashtests/856784-1.html b/gfx/tests/crashtests/856784-1.html
new file mode 100644
index 0000000000..b048f8d0e9
--- /dev/null
+++ b/gfx/tests/crashtests/856784-1.html
@@ -0,0 +1,11 @@
+<!DOCTYPE HTML><html><head>
+<style>
+@font-face {
+ font-family: test;
+ src: url(data:font/opentype;base64,AAEAAAANAIAAAwBQT1MvMkm5zgUAAADcAAAAYGNtYXAMOQzXAAABPAAAASpjdnQg7x+UzAAAAmgAAAc6ZnBnbQjouigAAAmkAAAF12dseWZb0FHdAAAPfAAAOyhoZWFkzwIhqAAASqQAAAA2aGhlYRJ+DK0AAErcAAAAJGhtdHjugxS+AABLAAAAAOBsb2NhAAX85gAAS+AAAADkbWF4cAcxEgMAAEzEAAAAIG5hbWVqLhwrAABM5AAAAihwb3N0AAMAAAAATwwAAAAgcHJlcEEhZAMAAE8sAAAQbwADAiQB9AAFAAACigK7AAAAjAKKArsAAAHfADEBAgAAAAAGAAAAAAAAAIAAAAEQAAACAAAAAAAAAAAqMjEqAAAAIP//Bz7+TgBkCBUDAwAAAAAAAAAAAAACywAAACAAAwAAAAEAAwABAAAADAAEAR4AAAAoACAABAAIACAAKgA6AEMARQBHAEkATgBQAFYAYgBmAGkAbwB1AHggrOAC//7//wAAACAAKAAtAEEARQBHAEkASwBQAFIAYQBkAGgAbAByAHggrOAA/////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAKAAoACwARgBKAEoASgBKAFAAUABYAFoAXgBgAGYAbABsAGwAcAAAAAMAHwAgADEABAAiABQAGwAdABkAHAA0ADMANgAyABoAIwAhACQABQAuACcAJgARADUAKAAsAA4ALQApACsACgAXACoACwAWACUACAANABUABgAHABAAEgAPAAwAGAAJABMALwAwAAIAHgA3AAEAAAW6ABwFugAcBacAHAQmABwAAP/kAAD/5AAA/+T+af/kBboAHP5p/+QC6gAAAR0AAAEdAAAAAAAAAAAAsgCsANcBKAEgALMB+gAXAPgBGQExAEkABAD3AAMArwD9AJUAFABUAJYBEgAkABYAVQBJAQQBGQErAIwBm/92/+kAPQCSAKL/twGC/6oAFgCPAMYA+AAcAN4EAQA3AE4AVQBVAGUA6QPlAFn/mgAIAIcACwA7AFIBFgBhANYA1gD1AAAAkwCUAL4BfP/4AAQAFACCAJIAPABBAEH/wf/8ACoAjASQBdgJtQCRALsBBv9j/2kAHgAiAIoCK//W/98AJgBZAKMArAEEASsBwARIACEAawCFAJgBGQPGAGsAlQCkAP4BDAJdA0MFvwAAAEkAVgBuAHcAigCqAMoBEgFQBdgF8P97/+cABgATACgAYQBpAOkBNQFNAqUEDP8+/9oAWwC5AMgBGQEZARkBwARbBKcFW/4//53/wgAVALcBCgG8AcEFMgWO/YH/of+uAAwAJgAxAD0ATgBWAGIAgwDBAMkA8QDyAn//fwBIAFMAdwDFAR0BIAEmASgB1gIZAn4CfgPTAC4AQQBdAGsAdQCfALAAsgC6ALsAvQDWANsA4ADlARQBGwFKAWIBkQHyAgwCZALPA5sDtAPUBAEEqQAWACMAJQAqAHQApQC2AMwAzQDPAQUBIAEwAVABagFvAZcBnQHgArAC7AL3BAgEgwT7BP0FJv7g/vv/Tv/1ABgAGgBMAHoAfwCRAKMAswC0AM4A1QDyAPMA9gEQATgBaAGhAbAB4AHsAgkCIgJPAnAClgKlAq0DTgORA8EENQRCBGsEzQTaBYYFiwdhB/78pv6T/q3+0f+3/9EAAwAOABgAJgBGAGkAgQCPAKUAvwDTANUA2QDdAOIBGQErATgBOwFaAV4BaAFzAYgBlAGtAcUB0QHqAfICAAIAAgACIgI7AkQCTwJvAnICfgKCApMClAKlAs8CzwLQAtoC3QLrAvUDBQMiAzYDcQOhA7ADuAPQA+YEEAQmBC4EMQRPBFoE/wUyBTIFRwVTBagFqwXCBfAGPAZkBnAG6AeCB4QIzP0q/d7+AP5o/rD+s/+qAAgAWQB6AJEAngCiAK8AtAC7AMoAzADOANkA4AD0ARQBGgEhAScBKwE5AUYBSwFNAVcBXAFlAYIBhwGSAZgBmwGiAa4BxQHFAdECBwIiAisCQQJTAmECZQKEAocCjQK0ArQCugLJAtYC2ALtAvUDFwMjAysDMQNJA1oDWwNuA3EDdAN+A4QDkQORA6oDzwPTA+cD6APtBAgEFwQeBHUEegSZBKcEtATRBUwFbQVtBaIFvwXABdEF/AX8BgIGGgYcBi8GagaoBuIHBgc2B1AHiQfUB/MIcAEcASoBGgEgAAAAAAAAAAAAAAAAAhkACwAeAqoCFAR/Ae0AAAAdAQQADwCRACsBiAFTARIB8wA/A/4BaAEOBH8B7QNuAxUCGQQTAAAAAAZABLAAAAJ0AbsANQHFAH8GAgMBAAAE4ACyAdwC4ATDAj0A1QFgARkEpwNuBcoCIQCrBCYAkAK8ArsBQgC0AjwCVgKcAwAB5QGoAOUAawB4AJQBawFzAKsB7QE6AX0BNwF/ANQCFgNTAYQAPP+iAgQBCQFJAfAAbgMVAIEEZABeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATkA3ADp/p4EDQR8ASsAuACWAFkArADfAakA+gEF/+wAFwADAFUAYQAEAIwAowCFACgBIABdANYAfwEmARkBBAFsBs8AtAEGAAAHNwY+BHoA8AD5AOkFugQmBEIAAP/n/mkEngTj/zf/LQEgAQUBIACoAHQAaABHAPIA5QDZAL0AqABoAEcAXABIAAoAKAAyAEEAUABaAGQAfQCHAJH/sP+c/4P/ef9vAMsBIAD6ASwB+gGgANUAuABcADwAyADIAI8A2QGLALMARwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP5kAMAA6gEYASUBMgOwA+0FdgWQBaoFtAW+Bc0GMQB4AIQAmwDMAOIA9AEKASABYwDRAOoA9wEIAUIAGQAsADQAQQA4AEgAWABsAlkDvQBDARoAcADTACgANwBCAFAAWgBkAHMAeACCAIwAnAClAL0AzgDwARABXAC+ANgBAgEXASwBYwDqAQgAQQBLAFUAXwBzAKYBCQGDAbMAQQBkAB4AKgDrAPoBDgE4AnQALABAAIIAlgC2AMAAzADcAOYA8AD/AQoBIAEsATsBRAFWAWMA9wBXAGQBEAE2AFABsQAA/7YAOQBOAEQDzADlACQBEABCASIBpADwAGAA4AAOAB0AOQXjAQIALP5O/zgCaQO9ARYA/wAOAKAAVAAbAD0BcQBBAA8AUAD9ABUBTwA1/lIALADTAABAQVRAPz49PDs6OTg3NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEALEUjRmAgsCZgsAQmI0hILSxFI0YjYSCwJmGwBCYjSEgtLEUjRmCwIGEgsEZgsAQmI0hILSxFI0YjYbAgYCCwJmGwIGGwBCYjSEgtLEUjRmCwQGEgsGZgsAQmI0hILSxFI0YjYbBAYCCwJmGwQGGwBCYjSEgtLAEQIDwAPC0sIEUjILDNRCMguAFaUVgjILCNRCNZILDtUVgjILBNRCNZILCQUVgjILANRCNZISEtLCAgRRhoRCCwAWAgRbBGdmiKRWBELSwBsQsKQyNDZQotLACxCgtDI0MLLSwAsBcjcLEBFz4BsBcjcLECF0U6sQIACA0tLEWwGiNERbAZI0QtLCBFsAMlRWFksFBRWEVEGyEhWS0ssAFDYyNisAAjQrAPKy0sIEWwAENgRC0sAbAGQ7AHQ2UKLSwgabBAYbAAiyCxLMCKjLgQAGJgKwxkI2RhXFiwA2FZLSxFsBErsBcjRLAXeuQYLSxFsBErsBcjRC0ssBJDWIdFsBErsBcjRLAXeuQbA4pFGGkgsBcjRIqKhyCwwFFYsBErsBcjRLAXeuQbIbAXeuRZWRgtLC0ssAIlRmCKRrBAYYxILSxLUyBcWLAChVlYsAGFWS0sILADJUWwGSNERbAaI0RFZSNFILADJWBqILAJI0IjaIpqYGEgsBqKsABSeSGyGhpAuf/gABpFIIpUWCMhsD8bI1lhRByxFACKUnmzGUAgGUUgilRYIyGwPxsjWWFELSyxEBFDI0MLLSyxDg9DI0MLLSyxDA1DI0MLLSyxDA1DI0NlCy0ssQ4PQyNDZQstLLEQEUMjQ2ULLSxLUlhFRBshIVktLAEgsAMlI0mwQGCwIGMgsABSWCOwAiU4I7ACJWU4AIpjOBshISEhIVkBLSxLsGRRWEVpsAlDYIoQOhshISFZLSwBsAUlECMgivUAsAFgI+3sLSwBsAUlECMgivUAsAFhI+3sLSwBsAYlEPUA7ewtLCCwAWABECA8ADwtLCCwAWEBECA8ADwtLLArK7AqKi0sALAHQ7AGQwstLD6wKiotLDUtLHa4AjYjcBAguAI2RSCwAFBYsAFhWTovGC0sISEMZCNki7hAAGItLCGwgFFYDGQjZIu4IABiG7IAQC8rWbACYC0sIbDAUVgMZCNki7gVVWIbsgCALytZsAJgLSwMZCNki7hAAGJgIyEtLLQAAQAAABWwCCawCCawCCawCCYPEBYTRWg6sAEWLSy0AAEAAAAVsAgmsAgmsAgmsAgmDxAWE0VoZTqwARYtLEtTI0tRWlggRYpgRBshIVktLEtUWCBFimBEGyEhWS0sS1MjS1FaWDgbISFZLSxLVFg4GyEhWS0ssBNDWAMbAlktLLATQ1gCGwNZLSxLVLASQ1xaWDgbISFZLSywEkNcWAywBCWwBCUGDGQjZGFksANRWLAEJbAEJQEgRrAQYEggRrAQYEhZCiEhGyEhWS0ssBJDXFgMsAQlsAQlBgxkI2RhZLgHCFFYsAQlsAQlASBGuP/wYEggRrj/8GBIWQohIRshIVktLEtTI0tRWliwOisbISFZLSxLUyNLUVpYsDsrGyEhWS0sS1MjS1FasBJDXFpYOBshIVktLAyKA0tUsAQmAktUWoqKCrASQ1xaWDgbISFZLSxGI0ZgiopGIyBGimCKYbj/gGIjIBAjirkCpwKninBFYCCwAFBYsAFhuP+6ixuwRoxZsBBgaAE6LSyxAgBCsSMBiFGxQAGIU1pYuRAAACCIVFiyAgECQ2BCWbEkAYhRWLkgAABAiFRYsgICAkNgQlmxJAGIVFiyAiACQ2BCAEsBS1JYsgIIAkNgQlkbuUAAAICIVFiyAgQCQ2BCWblAAACAY7gBAIhUWLICCAJDYEJZuUAAAQBjuAIAiFRYsgIQAkNgQllZWVktAAACAQAAAAUABQAAAwAHAEK0AgH+Bge4Aj9AEwAFBP4DAAoHBP4BABkIBgX+AgO8ASYACQGwARgAGCsQ9jz9PE4Q9DxN/TwAPzz9PBD8PP08MTAhESERJSERIQEABAD8IAPA/EAFAPsAIATAAAABAEEBhwJpAqAAAwAyQCEwAUABAgE3AAADEAMgAzADBANEBRAAIAAwAAMAGQSlbBgrThDkXU0Q5l0AL+1dMTATESERQQIoAYcBGf7nAAADAJYAAAViBboAEwAgACwA2EA/dyoBaA54KuYE9gQECSEVCQYoLCElFhIfFU8VAjAVrxUCFRUUIyIlEhMIIBQlAQACGydwBoAGAgZLKCevDAEMuP/AswkLNAy4AoxAITAuQC5QLmAucC6ALpAuoC4IIC4wLgIuFCIgACATMBMCE7gCi7MtMVMYK04Q9F08Tf08TRBdcfYrcU3t9F3tAD88/Tw/PP08ETkvXXFDXFi5ABX/gLIdORW4/8CyGjkVuP+AsRM5KysrWTz9PAEREjkAERI5MTABS7ALU0uwD1FaWLEKIDhZAV0AXRMhMh4CFRQGBxYWFRQGBgcGBSEBETMyNzY2NTQmJyYjAxEhMjc2NjU0JiYjlgJKrquHWm9fhpBdoXZK/uX+DQEowq0qTFdLSizRqgESoCtCU0B5ygW6HVyZX2esKye8f2S9cQ0IAgTG/q0FCVdHRFUJBf25/ngJDF1OQlwqAAACAJMAAAGsBboAAwAHAHe5AAn/wEA/EQo/QAlQCQKACbAJwAnQCe8JBR8JYAl/CaAJsAkFAwYHAAUEAw8AAUAA0ADgAAMAXQIBAAYFBgcECgIHJgEEuP/AQAkhJDQEGQg/PBgrThD0KzxN/TwAPzw/PD88/V1xPAMFEDw8EDw8MTABcV1yKxMRIREBESERkwEZ/ucBGQS2AQT+/PtKBCb72gABAJMAAAGsBboAAwBTuQAF/8BAKREKP0AFUAUCgAWwBcAF0AXvBQUfBWAFfwWgBbAFBQIBAAMACgIDJgEAuP/AQAkhJDQAGQQ/PBgrThD0KzxN/TwAPzw/PDEwAXFdciszESERkwEZBbr6RgACAEH/6AQnBD4AFAAcAaO5ABD/+EBGCzmZCZoNlhCoBacKuwm7DbgaCAgUAUgCRwZGCk8eqA22BrYaxwrIDNYK2Az4B/cNDRwPHBVAGx00FUAOETQPFb8VzxUDFbj/wLMPHj8VuP/Asw4XPxW4Ao1ADA8OElAOYA4CDhgSAbj/wLYZGzQBMxIAuP/Asx0gNAC4/8CzIik0ALj/wLMrLTQAuP/AsxgcNAC4/8BADw4PNKAAAQAAEAACAF8SErgCdLMECxIYuAJ0QCQLBwAhAS8VIU8OAQ4aLx5fHm8enx4EHg8hCEANDzQIGR1pQRgrThD0K03tThBd9l1N7fTtAD/tQ1xYQBQYQCgUPxhAHg8/GEAbED8YQBwRPysrKytZP/1DXFi5ABL/wLMoFD8SuP/Asx4PPxK4/8CzGxA/Erj/wLIcET8rKysrWfRdcSsrKysrQ1xYuQAA/8CyEjkAuP/Ashc5ALj/sLMJCj4AuP/AskEhPysAKysrWeQrERI5XUNcWEAUDkAPHj8OQBwRPw5AGxA/DkAOFz8AKysrK1kvPP0rK3IrKzwBETMxMAFdcQBdKwEFBgYjICcmNRAAMzIAAyEWFjMyNhMmJiMiBwYXAvoBGDbpr/7rhWkBFNPtARIG/UADgmFCWicDeFZcPDwBAVIvmqG1kd0BCAEr/sf+vX2LSAFsen9DQ3MAAAEAH//oApEFnQAZAM1AKSAAIAEjCikPOg5KDlkPBxkVABgDFhUAFxITFAEXEgIUARgDCQcKBwwYuAEBQA8AF6AXsBcDYBegF8AXAxe4AQSyFQEUuAJ0swAVBge4AnRADgwLCS8KLwAALwFfAQIBuAEEQCgYAyYXElUVPxSfFK8UA2AUgBSQFNAU8BQFABQQFCAUMBQEFBkaeKAYK04Q9F1xckuwN1NLsDtRWli5ABT/wDhZPE38PP089F08EPQZ5AAYP+0/PP08EPRdceQREjkRMw8PDw8xMAFdARUjERQWFjMyNxcGIyImJicmNREjNTM1JRECesALJxwnShhifEx6OQsJgYEBGgQm4P5UgiscG9oqM1FFMZUBz+DTpP6JAAEALAAABLkFugAHAHJAIy8JMAQwBVAJcAmACZAJBwYBBQIlBAMCBwAICRcXGgR/BQEFuAEtQAoGByABMAB/AAIAuAEtQBEDDlACcAKAApACBAIZCP2sGCtOEPRdS1FYsQJAOFk8TfRdPP089F08RWVE5AA/PD88/Tw8PDEwAV0hESE1IRUhEQHf/k0Ejf5OBML4+Ps+AAIASf/oBC4EPgAjADIBcUBoBxoIHAUdFhpKG0gcSSXbEN8RCTYZRhlXJmYZZyaGJpIZkxqmGrkbxxrIGwwGBg0VFgYZFicGKRVZGXcChgKmBrUGxgYMvzTZEAIdJDIxESwNJEArLjQkQCIoNCRAGR00byT8JAIkRh24/8BAMA4PND0dAQAdEB2wHfkdBB0dLAEzAEAODzQPAB8AAgBVIUAcET8hQBsQPyFAGBo0IbgCdLUEBwwNCiy4/8CzHBE/LLj/wLMbED8suP/AsxgaNCy4AnRAQBQLHjEmCCkJKA1ZHwyfDAIfDAH/DAEMQA4WNAwaTzQBNGAAAQCOMAEBATMpIV8XAd8XAU8XXxdvFwMXGTNpQRgrThD0XXFyTe30ce1dThBd9itdcXJN7fTk/TwAP+0rKys/PD/9Kysr9F0r5BI5L11xK7EGAkNUWLIvHQFxWe2xBgJDVFi5ACT/wLcbHTRUJGQkAl0rWV0rKysREjkDDhA8PDwxMAFxXQBdcQEnNjYzMhYWFQMUFhchJicmJwYGIyImNTQ2Njc2NzU0JiMiBgEGBgcGFRQWMzI3Njc2NQFl/yvSz7y4SwMbJf7qCxAHA0ikXaS9VpuSxUxQb0tUAV426iQ3WERMRTMQCwLiLpqUWYm3/riMhUwcNxkIRkayiFqNSxwlIBxRRTv+0hIyGCc8O1YyJjckZQABAIcAAAM3BD4AEACoQCiXBQEJDgFTBWYFdQUDLxJYDmgOcBIECgkPDB8MAo8M/wwCPwxPDAIMuAJ3QCkHBwEACgMCBgooAAkQCTAJcAkECRp/Ep8SAl8SfxKvEtASBBIQACYBA7gBKUALAgKAAaABAgEZET+5ARwAGCtOEPRxPE0Q7RD9PE4QXXH2XRlN5AAYPzw/PD/tXXFyOTIxMAFdAF1xAEuwF1NLsDVRWlixCjI4WQBdISERIRU2NjMyFwcmIyIGBhEBoP7nAQVDa0RgWVdHPTtSLwQml2tENfUuQar+8QABABgAAALmBdMAFgC4QDI2BAEqBCAQIBFZBIAYBQgEvxgCFRYRAhQSFhEOEw8AEA4TAQAQAhQJCA8LAV8L/wsCC7gCdEAKBgERDxYB/xYBFrgCdEAeEAAAAfAAAQAGExQKCTM/CE8IUAgDCCgQLxFfEQIRuAEEQA0OEyYCFF8AoBbAFgIWuP/AtgkMNBYZF3i5AmkAGCtOEPQrcTxN/Dz9PPxdPPRdGeQAGD88P11xPP1dcTw//V1xOTIPDw8PMTABcV0AXRMzNTQ2NjMyFwcmIyIGFRUzFSMRIREjGJw5mXV4cyZDPj010tL+55wEJlCGhFMkxBA5UUvd/LcDSQAAAQCYAAAFIwW6AAkBzkAOCQMGCBkDFwgEEggCAwO4/wCzEgs/A7j/wLNbXTQDuP/AQCpTVDQDMgcIFAcHCAMIAgIHAwkEAgIJBwgDBEBbXTQEQFNUNAQyBs8FAQW4AotAGUALUAtgCwNwC4ALAqALwAsCIAswCwILCAm4/8CzW100Cbj/wEAOU1M0CTIBIAAwAMAAAwC4AouzCjF1GCtOEPRdPE39Kys8TRBdXXFy9l08Tf0rKzwAPzw/PAEREjk5ABI5OYcuKysrK4d9xLEGAkNUWLkAA//gQAkOJzQIIA4nNAO4/8C3CQ00CEAJDTQAKysrK1kxMENYuQAD/4C2CzUIgAs1A7j/wEA9Gi40CFMaLjQFAxYDMgNAAwRGA4UIkAigCLII5AMGxAPPCNoIAyADLwg0AzsITwiSA58IoAOvCLADvwgLB7j/wEAJMzU0AkAzNTQHuP/gQA0vMjQCIC8yNAIHFDUHuP+XQAkhLjQCVCEuNAe4/8BARh4gNAJUHiA0CAIHBxgCAxcHLAInBzsCMwdOAkAHXAJWBwkUAhsHTQJFB5oHqwfLAtkC6ALnB/kCCycCKAdKB3gHiAesAgYBXXFycisrKysrKysrKwBdXXFyKysrK1kAXTMRIQERIREhARGYASACWAET/tf9sQW6/C0D0/pGA7z8RAAAAgBS/+gEmgQ+AA0AGQCXQEjoAecI9xP3FQTHAugFAhIZBRkJAlkQVhNWFlkYlwKYBpgIlwy4CdUC2wXcCdUM5wXnBugNEKcIywLMBsMIxgwFdQiJBoQIAxG4AnSyCgsXuAJ0QBQEBxQ5BxpgG3AbAhsOIQAZGlhBGCtOEPRN7U4QcfZN7QA/7T/tMTAAcV0BXXFDWEAJaRBmEmYWaRgEAV1ZAF0BXRM0EjYzMgAVFAAjIiQmJRQWMzI2NTQmIyIGUor9nPEBNP7J7JL+94oBIJZubpWVbm6WAiKMAQaK/sfv8f7DhP+onqiooJyoqAAAAQB+AAAGmAQ+ACcBO7kAKf/AQF0RCj8FBgYMFQYWDDQDNAg0GDQjRAJFCEUYRCMMIAMvKVMJYCmAKZ8ppAanB6YMtQa1DLAp0CngKQ4AKS8pUCmfKb8p3ykGKUAaHDQ/KVApgCnQKeApBQchBAcaHRa4AnSyCgchuAJ0QB4EBxAREScbHBwmJwoBAAYPECYSEUBaNWARAW8RARG4AkZADxobJh0cQFo1bxwBYBwBHLgCRrQlJiYnAbgBKbIAACe4/8CzDwk/J7j/wEA2EQo/J0BaNSdAQTUnQDw1J0AkJzQnQDo9NC8nzyffJwMPJx8ngCcDACcgJzAn/ycEJxko4zwYK04Q9F1xcisrKysrKys8TRDtEP089l1xKzz9PPZxXSs8/TwAPzw/PDwQPBA8EDw/7T/tARESOQAREjkxMAFyK3FdAF0BKxMhFTYzMhYXNjYzMhYXFhURIRE0JyYjIgYGFREhETQmJiMiBgYVESF+AQOLwGaWMEaiXHWiKB3+5x0nUTtoLv7nHj82QWgt/ucEJpGpVFVVVF9cRJj9WQJfni48SIuW/gICRptaLEaEmf38AAEAjAAAAbQFugADAG+5AAX/wLMyNDQFuP/AsyMlNAW4/8BAPxQXNAAFQAVQBYAF4AUFHwVgBXAF8AUEgAUBAgECAwAIAgPZAQAAsADgAAPAAPAAAiAAMADQAOAABABuBDGfGCtOEPRdcXI8Tf08AD88PzwxMAFdcXIrKyszESERjAEoBbr6RgABAJEAAARZBD4AFgCfQBgHExcTWghoCAS4BAE0CDQQRAhED+kQBQa4AnRAHREHDg0GDAsLAQAKAgEmFgBAICQ0rwAB/wABABoYuP/AQBYiJDSQGKAYAnAY8BgC7xgBGAoLJgwOuAEpsg0NDLj/wEAPICQ0oAwB8AwBDBkXPzwYK04Q9HFyKzxNEO0Q/TxOEF1xciv2cXIrPE39PAA/PDwQPD88P+0xMABdAXFdISERNCYmIyIGBhURIREhFTYzMh4CFQRZ/uckUTlJdCv+5wEFi9Ndmk8fAh6sZThQhLL+HwQmnLRDaIR7AAABAI3/6ARTBCYAFgCcQBdXEWcRlgUDCQYZBjwCPBFLAksR5wIHD7gCdEARBAsWAAoVFBQKCQYTFCYVFQC4ASlADhZAICQ0rxYB/xYBFhoYuP/AQBYiJDSQGKAYAnAY8BgC7xgBGAoLJgkIuP/AQA8gJDSgCAHwCAEIGRc/PBgrThD0cXIrPE39PE4QXXFyK/ZxcitN7TwQ/TwAPzw8EDw/PD/tMTAAXQFdITUGBiMiJiY1ESERFBYWMzI2NjURIREDTjq9aWuqTAEZH1I/SHIqARmfVWJeqpYCoP4Y4GU7T3XkAcD72gAAAf/9/+cCOwXTAAMAOEAdAAEBSQIDFAICAwIBAAMACgHrAhoFA+sAGQSTbBgrThD0Te1OEPZN7QA/PD88hwUuK30QxDEwBwEzAQMBa9P+kRkF7PoUAAABAJIAAARZBboAFgCyQCsPAR8BOQEzAjMQQgFCEd4B+QEJBwUWBSQCWBFoEQUBAQITFBESEwMUAgEPuAJ0QB0DBwkKChQVChYAAAsKJggJQCAkNK8JAf8JAQkaGLj/wEAWIiQ0kBigGAJwGPAYAu8YARgAFCYWFbj/wEAPICQ0oBUB8BUBFRkXPzwYK04Q9HFyKzxN/TxOEF1xciv2cXIrPE39PAA/PD88PBA8P+05OREXOQMOEDwIPDEwAV0AXQERNjMyHgIVESERNCYmIyIGBhURIREBq4i9YZxPHf7nIFE9Rm4z/ucFuv3ln0hwiI/9kQIxp1o1RImG/ewFugAAAgCH/+gElAW6AA8AHACduQAS//hAMQs5NxtHGwISVgZWClYWVhhZHPcHBjUEOw07EzUbRQRLDUsTRRuUB5kJCgwOAQIBABq4AnSyBQcUuAJ0QBMLCw8AChchCBpwHgEeECkCAyYPuAEpQAwBcACAAAIAGR0/QRgrThD0cTxN7f085k4QcfZN7QA/PD/tP+0/PDEwAHFdAV1DWEALZgZmCmYWZhhpHAVdWQBdKzMRIRE2MzISERAAIyImJxUTFBcWMzI2NTQmIyIGhwEZgrLC/v79uVuxQBI0SXldg4RnZYYFuv3wlP7n/vn+8P7aW1mcAiqlT3Cfq7ahnQAAAQCT/+cFJAW6ABkAikA4BwgHCQcQFwgWCUcIRwkHVwlWEJYQlxGYFZsWpxC3FtcV5Qb2BgsNDAwBAAIHJRMJDAsgDc8OAQ64AotAIkAbUBtgGwNwG4AbAiAbMBugG8AbBBsBAiAAIBkwGcAZAxm4AouzGjF1GCtOEPRdPE39PE0QXXFy9l08Tf08AD/tPzw8EDwxMAFdcRMhERQXFhYzMjY2NREhERAOAiMiJiYnJjWTASgLE498foAaASgwgdiu0tl+FB0FuvzmvThabWeWrgMr/P7++NqWWWGbVX72AAABADD/6AQQBD4AKgLCQMAGEQYjCCcXERcjmBKYFJcnlSoJBxRGFAISuw25Dsch5SP4DfYiBikNVQ1lDZULlxKnIrkMB0EjQCREJmciZCaHEocUhiKDJAk3JkUGRgtKDU8PRiFCIgciJCcmNww1ITUiNSM1JAcGCgURCSEYDScMIiIiIwckIkAscwx4FHkVdil1KogVhCqaFZUqtCK0Iw2AAY8XjBiZKqkqsCwGFyEWQCEjNBZAHB80HxYB3xYBFjMIIVAljyUCJUAYHTQlGiy4/8BAFxEKP1AsATAsAS8sASweITAQARAzASEAuP/Asw8JPwC4/8CzEQo/ALj/wEAJCQ00ABkreLgYK04Q/CsrK03t9HHtThBdcXIr9itxTe30cXIrK+0AsQYCQ1RYQDUGAQEGARYBJiI2IUYhVAFZF2QBaRf2AQoBFwIEGiEiAigTDQwCKBpfBAEERigLUBoBGkYTBz/9XT/9XRESFzkREhc5ERIXOV1xG7kAIv/LsygqNCG4/8uzKCo0Irj/4LMeJDQhuP/gsx8kNCK4/+CzGRo0Ibj/4EAbGRo0aw0BNiJGIpgNlCLEItQiBiEiDA0EBBoAuP/AtRkbNAAzAbj/wLMXLT8BuP+wswkKPgG4/8CzIiU0Abj/wEAdGhw0AAEwAUABUAEEYAGAAfABAwABEAFQAWABBAG4/8CzExY0AbgBAUBNAAQBXwTwBAIERigLFkAZGzQWMxdAFy0/F0AJCj4XQDU3NBdAKy40F0AlKTQXQBocNA8XHxdfF28XBBdVGkAiJDQPGgFQGv8aAhpGEwc//V1xK/RdKysrKysr5Cs//V1x9CtdcXIrKysr5CsREhc5XXErKysrKytZMTABcV0AcXFxcV1dQ1xYuQAk/8lACQsSPw8oCxI/Ibj/7LYNOQwUDDkhuP/ssgw5Irj/6rELOQArKysrASsrWQBxXRMlFhYzMjc2NTQnJickJyY1NDYzMhYXBSYmIyIHBhUUFxYEFxYVFAYjIiYwARoSbmNtNyUUFUn+rFt+2uXa1Cj+9xFfWG8wIBwmAcFZWPTv2f0BLytSVSgcLyAVFBFLPlaZiryOizE+Qh8WIx4VHGZKS4aS0rAAAQAzAAAEDAXAAB0BP0BftRi2GrkbygTHGNAY0BnQGghDG0McQx1WGZsElRiqBKYcCAYaIAAoBjcaSARDGEMZQxoIJBgkGSQaAxYmBFYEiBicG5wcnB2qHKodCBIAHRAdIB0xHXYdhB2QHdYdCB24/8BAFhQVNB0CEAwPHRAAIAACIAAwAEAAAwC4/8CzEhY0ALgCobMCAQwPuAFWQCMfDC8MAgxAEhY0DKYTBQnYFhYBTwABABofD9gQdwIZHtPCGCtOEPRN9O1OEPZxPDxNEO0AP/0rceQ/PP0rXXE8ERI5AREzK11DXFi5AB3/wLIROR24/8CyDzkduP/AQA4QOQQIEDkFCBE5BAgROSsrKysrK1mxBgJDVFhACwkbGRsCGxMBBBMAABESORESOV1ZMTABXUtRWL0AG//gABz/4AAd/+A4ODhZAXFdXV0BESE2Ejc2NzY1NCYjIgYHJTYkMzIWFRQGBwYEBgcEDPwnEKDsvis6ZVlYaAj+6BkBCMbZ+EdNM/72RxYBBf77lAEJ27E/V1VeZWp7HOjK6q5js2JB9FAmAAADAFP/5gQXBcAAGAAkADABDbUwCB0fNCa4//hAbB0fNMcRxxPXBdcHBHUQdhSEEAMmACoMNgA7DEYATAxuBGMIZxFoFXcnhyeXDZgYpA2pGKkaph6nJ6YsqTC5GrceF3cThhOGFIcnBJcMAQyXAAEAHC6YDAEMKwmXAAEAJQMuQBIWND8uTy4CLroCjgAc/8BAEBYYNHAcgBwCoBwBHBwGEii4/8BACRIWNDAoQCgCKLgCjkANEg0/Ik8iAiJAEhY0IrgCjkAaBgUf2Al3K9hPDwEPGjIZ2AN3JdgWGTHTwhgrThD0Te307U4Q9nFN7fTtAD/tK10//V0rEBE5L11xK+1dKwEREjldERI5XQAREjldOV0xMAFxXQBxXSsrASYmNTQ2MzIWFRQGBxYWFRQEIyInJjU0NhMUFjMyNjU0JiMiBgMUFjMyNjU0JiMiBgFIbWPl09HnamB6f/7918iFnXa5X09QYF9OUWAad1lXcnRZZ2UDFy6hYKTW1qRmnyoxvHvL/ml82HfHAVFUXl9UT19g/T10gn12Z32OAAACAFb/5wQOBcAADgAgAJNAS3gKiAqnAaoHqgmnDrcJyAkIVhFZFlkaVh9nEWgWaBpnHwg5AjkGNgk2DUkCSQZFCUYNpwnLAskGxAnEDdkC2wbUCdQNERAYIBgCGLj/wEAlEhY0GKYIDR8PLw8CD0ASFjQPpgAFHdhPBAEEGiIU2AsZIdPCGCtOEPRN7U4Q9nFN7QA/7StxP+0rcTEwAV0AXV0BMhcWERAHBiMiABEQNzYXIgYHBhEQFhYzMjY3NhEQJiYCMtV4j5B31db++pB31TNQFh00TzMzUBYdNE8FwJi0/l/+YLaWAUkBpgGetpbpQVRt/v7+/sFAQVRsAQIBAsFBAAEATf/nBBsFwAApANlAMocVyRUCexyLHAKmA6kFpxS2A7oFthTaGN0ZCBYUAY0WjRcCIQoNAAQBFxMWIR8NEAwKuAEkQAxPDQFADY8NAg0NARa4AQJADx8TLxMCE0ASFjQTphsFAbgBVrUQBCAEAgS4/8BAMhIWNASmJw2wDMAMAgwMFhDYfx+PH58frx+/HwUf4AfYTyQBJBorFtgXdwHYABkq08IYK04Q9E3t9O1OEPZxTe30Xe0ROS9dAD/9K3HkP/0rceQROS9dce0BERI5ETkAERI5ERI5ERI5XTEwAXFdXQBdEyUWFjMyNjU0JiMiBzcWNjU0JiMiBgclPgIzMhcWFRQHFhYVFAAjIiRNARANclFXd3JSNksfcnhYSUhmC/79G23Dec99Z9N+l/7m0sf++gGFIWhuhHBqfBXlA2lXSlhkYCyFn1uEbIjBcxu8hcH+8OUAAQCiAAADJgXAAAkAVkAJawJ7AosCAwIEuAEps18FAQW4Al63CAkFAQAMCQC7AVgAAgABAl1ADQUABB8EIASwBAQEGQq6AacBoAAYK04Q9F08TfY8/TwAPzw/PPRd7TkxMABdISERBgc1NiQ3MwMm/uea0W4BAjDkBCOQRf8kyYYA//8AQf/oBCcF0wImAAgAAAEHADcA6AAAADFAEQLgIPAgAiAgTyAC4CDwIAIguP/AQAsOETQgCyhIKwIBILkCNQApACsBKytdcXE1AAABAGv+UQJoBdMAEABGQA4oD6cDAggJAQAJEAASCL0BIgAJAAEBIgAAApRACgnzDJsgBDAEAgS4ApazEaVrGCsQ9l3t/fTtEO0APz8QPBA8MTABXQEjJgI1EBM2NzMCAhUUEhcWAmXBmaBjVoTAiWc9NSP+UecB8ukBIQEC4L3+0f5X7qT+qJtmAAABAEP+UQJABdMAEABJQBgnAicKZwJnCpcCpwKoDgcJCBAACBAAEhC9ASIAAAAJASIAAAKUtAjzBZsMuAKWsxJqQxgrEPbt/eTtEO0APz8QPBA8MTABXRM+AzU0AgMzFhIVFAcCA0VTRDocZom/l6dCS6z+UbK++N917gGpAS/X/h740e/+9P77AAACAMkAAAHiBCYAAwAHAC5AGwM4AQYGOAQKCa8DAgIGJgEgBTAFAgWvCM2rGCsQ9l08/TwQPPYAP+0/7TEwExEhEQERIRHJARn+5wEZAw0BGf7n/PMBGf7nAAABAJMAAAGsARkAAwAkQBUCOAAKAiYPAB8AIAAwAAQAGQRndhgrThD0XU39AD9N7TEwMxEhEZMBGQEZ/ucAAAIAQf/mBBQFwAAXACMA0EBYOxFLEWULegiJCKkFqQimDqYTtQC5A7UOuBG0E8UAyhHAExE0E1YLWQ1fEVITYBMGGQV3FpkX3RDfFAVoEwEABAEHGBIfGy8bAhtAEhY0G6YPBRAhICECIbj/wEAQEhY0IaYwCQFPCd8J8AkDCbgBT7OgAQEBuAEhtRAEIAQCBLj/wEAbEhY0BKYVDRjYTxIBEholAdgAdx7YDBkk08IYK04Q9E3t9O1OEPZxTe0AP/0rcfRd9l1x7StxP+0rcQEREjkAERI5MTABcV0AcV0TJRYWMzI2NwYjIgI1NAAzMgAREAAjIiYBNCYjIgYVFBYzMjZdARAKVEVXehFqn637AQnN3wEe/tbvrNQCXn1STmdwVFFvAVMeU1Cg/HsBC9bfARH+p/51/m7+nLcDHIiWe4yOhYAAAgAAAAAFvwW6AAcACgFBuQAH/9hACTc5NAYoNzk0B7j/wEAJKDU0BkAoNTQHuP/YQFAhJzQGKCEnNCkAKgQqBSgKLww4ADcFPwxqAGoCZQNmBWgIZwroAw9KBgECCAkBAwoJCQQHCQEBIAAHFAAABwYJBAQgBQYUBQUGCApAGh0+Crj/wEALGh00CiUCAwMGBAm4AbxADgYHAgUEBAEACAwXFxoAuAJhQAsfAQEgATABgAEDAbgCJEAJHwkBMAmACQIJugIkAAQCYUAJIAUBBRkLXmMYK04Q9F1N/Rn2XXH0XXEY/U5FZUTmAD88PBA8PzxN7RESOS88/SsrPIcFLiuHfcSHLhgrh33EBxA8PIfExLEGAkNUWLQJNAkNNAArWTEwAUuwC1NLsB5RWli5AAP//rIIBAq6//4AB//8sQYEODg4ODhZAXFdKysrKysrISEDIQMhASETAwMFv/6+gP22ef7GAjsBOSrKxgFN/rMFuvyKAiD94AAAAgBU/+gEYQW6AA8AHACQQC0SWQZZClkSVhZWGFkcmAeZCfgJCXAegB4COgM0DDoVNBlKA0QMShVEGZkJCRS4AnSyBQsauAJ0QA4LBw4PAAEAChcpDg0mAbgBKUAPDwAajx4BHhAhCBkdWDwYK04Q9E3tThBx9jxN7f085gA/PD88P+0/7TEwAF0BcV1DWEANaQZpCmkSZhZmGGkcBl1ZISE1BgYjIgAREBIzMhcRIQEUFxYzMjY1NCYjIgYEYf77QbFat/77/sKyggEZ/RIvRHphiIRnZIecW1kBJwEIAQ4BGZQCEPxwqkxupaS3oZ8AAQBi/+cFvQXTACAA2EBGOB5LHlYHdgh2DIUIhAyFF4QbCQYXBhsSFxIbKBEoGCgaKB4ISAtbBFQJWgtqBHsEehh0GrYOthDHDcYQ1xDnEA4DHAYgALj/wEAfGjkfAAEAJQIBARYcLQYJEkAOEjQSS08TARNAFRg0E7gBKEAqFi0PAwABASAZXxMBEycSVgIfICADAhogIjAiAiIZJ6AKAQ8KHwowCgMKuAKMsyF+nxgrThD0XXFN7U4QXfY8Tf08EPTtcRESOS88AD/99Ctd5Cs/7RE5Lzz9cSs8ERI5MTAAXQFdXQE1IREGBCMiJAI1NBI3NjMgBBcFJiYjIgYVEBIzMjY3NQM/An5d/p+15v6qrMC5jdIBEQEzLP7aH6uAwuXovF27QwIb9/24WonBAWfT5QFkX0nlyjdsffby/vv++0k0ugABAJUAAATwBboACwCQQD0IBQQHCCUGEh8FATAFrwUCBQUJAwQlAgECCgklCwAIBwZLAwJICgALAQsaIA0wDUANAw0ECSABIAAwAAIAuAKLswwxUxgrThD0XTxN/TxOEF32XTxN9Dz0PAA/PP08Pzz9PBE5L11xQ1xYuQAF/8CyHTkFuP+Asho5Bbj/gLETOSsrK1k8/TwDBRA8PDEwMxEhFSERIRUhESEVlQQ//OkC4P0gAzMFuvj+u/f+cfcAAQCdAAAEpQWuAAUAPUAaUAcBAgECBAMlBQAIBAUaBwIDIAEgADAAAgC4AouzBjG5GCtOEPRdPE39PE4Q/jwAPzxN/Tw/PDEwAV0zESERIRWdASgC4AWu+0n3AAACAJYAAAW8BboAFQAhAPhAgjkPSQ9XB2oLagyqCacOoCO2DtgJCgYIBgoXCBYKNg5GDkYPBwgQCREUDhQPFBA2DjYPRw91DnkQ0woLeAl4GXYdiAmIGYYdBgkWFAkMDw5TDnUOhA6UDqMOBQ4gDQwUDQ0MDwwVDRcWJRMQFAFgFKAUAhQUACAhJQIBAg0ODhUACA64AbxAJwANEA0CDdQbJ6AGsAbABtAGBAaHcCMBICMwIwIjIRUgASAAMAACALgCi7MiMWMYK04Q9F08Tf08EF1x9l3t9F3tAD88PBA8Pzz9PBI5L11xPP08ARESOTmHLitdDn0QxAEROQAREjkxMABdAXFdXTMRITIWFhUUBgcWFhcTIQMuAiMjEREzMjY2NTQmJyYjI5YCb+vVgMLBYH1qs/6e1nJUXmY829VqPE9IJLTnBbpPyoKl1xw4hqv+4gE/q1kh/ZwDTiRYQkpbDAUAAAH//wAABVQFugAGAQ6zAAMBCLj/gLISOQO4/4BAVBo6NMAI1gHWAtkE2QUFdgJ5BHgFlwGWApkEmAWfCAgDAAsGLwhnAmgEYAh3AQcZABYGKQAmBkkARwZXAAcAAwICIAEAFAEBAAYDBAQgBQYUBQUGA7gCYkALBgAIBQQEAgIBAgW6AmEABP/AQA8SOQsEAX8EgATfBOAEBAS4AQtACn8DgAPfA+ADBAO4/8C1EjkLAwEDugELAAICYUAJMAEBARkHXmMYK04Q9F1N/Rn0XStd9F1dKxjtAD88EDwQPD887YcFLiuHfcSHLhgrh33EsQYCQ1RYuQAD/8CzECc0A7j/wLUJDTRUAwEAXSsrWTEwAXFdXV0AKwErAF0hASEBASEBAgv99AFBAXMBZwE6/fMFuvvDBD36RgAAAQBK/+YE8gXTACwB0EA9uRG4HbYoxiwEBxMHFRcTFxUYK2UFZSh0BngNdCjZDNYjDFkKVQ5VIlkjaAxmEmchaShnLHcdhh2WIQwSI7j/4LMeHzQjuP/gQGUZGjRRIlEjwSLBIwRxInEjgSKBI+Ei4SMGKwoqDSQiJCM5DTQjSwpLDUQiQyNqDWUjeQ16IokNiiKmCqcNqCITCQoJDQYiBiMZChkNFiIHIiMKDQQBF1YYQBkgNG8YAW8YnxgCGLoCZQAb/8BADBo5HxsBGy0UAwBIAbj/wEBJGiA0MAFAAVABYAGQAaABsAHAAQgB7gRAGjkQBAEELSoJGO//FwEXQBMXNBdLBycmGi4fJ6AQsBACEEsB7xEgADAAAgAZLdJTGCtOEPRdS1NYsQBAOFlN7fRd7U4Q9k3t9Cty7QA//XEr9F0r5D/9cSv0XXIr5BIXOV1dcXIrK0NcWLkAIv/gsxsdPiO4/9CzGx0+I7j/47ITOSK4/+CyEzkjuP/JshI5Irj/0EAPEjkNIBI5CiASOQogDzkiuP/oQA4MOQ0gDTkKGA05ChgTOSsrKysrKysrKysrKytZsQYCQ1RYQBU6CjoNNSI1I0sKSQ1DIkYjpgqpIgoAXVkxMABdcQFdEyUWFjMyNjU0JicmJyYnJjU0NjYzIAQXBSYmIyIHBhUUFxYEFhYVFAYEIyAASgEgGp+Hj5E9TDS57mCHf++pARQBFwf+2BN9fYFJLyw4AbDPdYz/AL/+6v7WAd0ckYh5UTRJGxIuO1Z5rnDDZvLKDXFjNSI5NCUvZm29i37cawEBAAABAJEAAAYZBboADAIYQAsLAyYIJgsDBAMBA7j/gEAJHDo0CiA6OzQJuP/gszo7NAm4/+BApBwuNAogHC40BgkICuMJ7AoEBAkKChMCHAQQCR8KIwIsBCAJLwpnAmgEZQlqCncCeASkCaoKtQm6CvYJ+goWnwSQCZ8KxgnJCtcC2ATWCdkK5wLoBOUJ6goNdwl4CoMCjASDCYwKkAIHWAtlAmoEZwloCnYCeQQHRAJLBEQJSwpXCFcJWAoHGAovDjQCOgQ0CTsKPw4HAwIMBAYJCQoVAhoEFwkHsQYCQ1RYQB8CBAMKCQUMBwcyBg4MMgAAAxADAghQCA00C1AIDTQDuP+AQA4LDTQIQA4nNAtADic0A7j/nEAQDic0AwsIAwEABAECBwoACAA/PDw/PBESFzkrKysrKytdAS/tENTtERIXORu4/ztALQMKCSAECAkJMgMEFAMDBAILCgoyAwIUAwMCCwgDAwwEAgIMCgoJCQcIHw4BDrgBDbMHBgUEugI4AAX/wLNbXTQFuP/AQBdTVDQFMgdAB38IAQi9fwMBA70LIAsMArgCOEASAQAAQFtdNABAU1Q0ADIfDAEMuAENsw0xdRgrEPRx7SsrEDzuEDwaGRD9cf1xPBoYEP0rK+4QPBDkcQA/PBA8EDw/PBIXOYcFLiuHfcSHLhgrh33EK1kxMAFLsBNTWLkACP/gsQsgODhZAV1dXV1dXXFyKysrKwArcV0zESEBASERIREBIQERkQG7AQoBBwG8/u3+3f7j/t4FuvwYA+j6RgSC+34Egvt+AAIAlQAABPgFugAPABsAd0AlBgW5FLkYA0cFAWcF1gUCEhElDQ4OABsQJQIBAg8ACBYnrwcBB7j/wLMJCzQHuAKMQBYfHTAdYB1wHYAdBR0QDyABIAAwAAIAuAKLsxwxUxgrThD0XTxN/TxNEHH2K3FN7QA/PD88/TwSOS88/TwxMABdcQFdMxEhIBcWFhUUBgYHBiMjGQIzMjY2NTQmJyYjlQHbAQ5Sfqpil05qycGir3ZDXkg1oAW6FiHdr4e4aREV/dcEwv5gLmJBUGgNCgABAGH/5wVeBdMAGgDWQE6GCYkUiRafAJgGxwnUA9QL9QMJJQkoDCgNKRQpFnUFdQmGBQgHEwcXFxMXFykCKgMlBQcoBZkFlwnJA8ULBT8BTwECAVIQAAHgAPAAAgC4/8CzERg0ALj/wLMKDTQAuAFaQBcYLQQIDkAOEjQOS18PAU8PAQ9AFRg0D7gBKEAiEi0KAw/vDlYA7wABTwECARowHAEcFSegBwEPBx8HMAcDB7gCjLMbflMYK04Q9F1xTe1OEF32XU3t9O0AP/30K11x5Cs//fQrK11x5F0xMABdAV1dXQEFBgQjIAAREAAhIBcWFwUmJiMiBhEQFjMyNgQ/AR9C/s3s/tz+iAF6ATQBDahkMv7bGqV2o8vIoHaqAhtb8OkBjwFaAW4BlZ9esEZyhOr++v7q7JYAAAEADAAABGAEJgALAZhAiygHyAQCmAe5AdwB1QfwDQUIBxoEGAYoBjcAOAhIAVkBfAF1BwomASsHNgE6B0YBSgeYC/gG+AcJJQQmByoKNAQ6CkMETgrDBAhMClQEWQpkBG0KeAF9CpQElge6CtUE3Ar8Cg0DBAcHCQoWBCAEKgozBD8KRgQJFQQZCjoKTgprCqcEtwTJCvYECQe4//CzEhg0BLj/2LMVFzQEuP/gQBUMETQBBAoHBAACAQQKBwQIAAkIAwO4/+C2KS00/wMBA7j/4EAPFiQ0AyYCCRQCAgkFBgsLuP/gtiktNPALAQu4/+BAFxYkNAsmAAUUAAAFBgUFAwIGCAkJCwAFuAJtsgYzCbgCbUALCGVPDZ8N4A0DDQO4Am2yAjMLuAJtQBLwAAEAABAAIAAwAAQAZQzEoBgrGU4Q9F1xGE3t9O0ZTRBd9hhN7fTtAC88PBA8Pzw8EDyHBS4rK3Erh33Ehy4YKytxK4d9xAEREhc5ABESFzmxBgJDVFi1CiAJITQEuP/gsgkhNAArK1kxMAArKytdcXFyAV1xcXIzAQEhExMhAQEhAwMMAX/+kQFXvMYBSv6YAYn+p9jaAiMCA/7cAST+Cf3RAUn+twAAAf/g/+cEMwXTAC0BCkBuBysBAlUcAdUcAQMIGwESAhUTdROFEwOaBboFygUDCAVIBQIeAyoIHyE0pxMB0RMBAx4RIRgOAfgOAQ4KKAclKApYCgIKCgMVGBoHFxcXAgkXFS0aDQAsCAEBDgABAy0sAxAPCUAICAwXASAAFxe4/8BAMwkONBcvEQcMJyNAdR6FHtUeA1cepx4CAx4oJyhnKAICKAgjGCMCCQAjHyYmQAkTNCYgJy88zSsBEDwYL19eXTNfXRDFX11dGhBN/cQyEMYrARgQ1hoZzRESORgvGs3UzQA/Tf3GX15dETk//cZeXRE5ERI5L1083TwQ1l1xPN08AV9dMTBdKwBfXl1dXV9eXV9dcV9dAQMmIyIHBgchByEGFRQXIQchFhcWMzI3EQYjICcmJyM3MyY1NDcjNzM2NzYhMgQzO1Wjo2U5GAImIP3mAQIB/SD+OBk0ZKDAcoKt/ty8gCmUIGIBAoMgdyp9vQE0qAWW/utVdUJjmxckIyabaz12dv7OQciIy5sYGy4jm8eGygAAAQAcAxgC8QXTAB4Ay0AliRMBVR1WHgJFHUYeAh0MCwUYBQ8OCwwKGAUdAxYAG4YcKwCGAbgBJkAUCRblFQnlQBUrsAoBCogPDgAVPha4/8BAJxETNBY+G3IgHEAaHDRPHF8cAhw+D+VADkAaHDRPDl8OAg4+AHIgAbj/wEAVERM0AT4KPg8JAT8JjwkCCRkfonwYK04Q9F1xGU3kGPQrGhn99F0rGhj9GfRdKxr9GPQrGeQAGD889F3kGuwQ7RkQ9vQY9BnkERIXORE5OQEREhc5MTAAXV1dEyc2NzY3JicmJzcWFyY1MxQHNjc2NxcGBxcWFwcnBvWXSE4fCBl2VRs7g2cYshsUQlpMNW+SeCQVmYc9Axh1UUoeCAQdFQqwNUCjZ0nDCB8pHbUZGIcpGmXfbAABAFcAAAQYBaYACwCGuQAE/+BAMQ8RNAoLGgs6BDgKSAVWC6oLvgvNC9kLCiELAQsDBwAfCy8LAi8LPwtPCwMLQBIWNAu6AqAAAwGstwICAQQHCAwIuAFYsy8HAQe4AmBADgJPAwEDGg0BABkM08IYK04Q9DwQ9nE8TfRd/QA/PD88EO39K11xPAEREjldMTABXSsTESEVBgICFyESEjdXA8F39oEB/vEH7cYEoQEFzHX+Sv4TwgEwAnj5AAEAW//nBDUFpgAdARBAKQgOIAw3EkUSSRmZDZ4OlxLaDgkSERMSIREjEoUSBQAEAQ0KDAwNEhERuAKgQBYODRQODg0SChQgATABQAEDUAGQAQIBuAFWtRAEIAQCBLj/wLcSFjQEphsNDLgCWkANHwovCgIKQBIWNAqmFLj/wEALFBY0IBQwFEAUAxS4AatAFBERHxAvEAIvED8QTxADEEASFjQQuAKgQBMPDw4EDxDgB9jQFwFAFwEXGh8OuAEhQBINdwG80AABQACfAK8AAwAZHtO5AUcAGCtOEPRdcU3t9OROEPZdcU3t9DwAPzwQ/StdcTwQ9l0r/Stx5D/9K3HkXXEREjmHBS4rDn0QxAEROQAREjkREjkxMAFxXRMlFhYzMjY1NCYjIgcnEyERIQc2MzIAFRQHBiMiJFsBGAx2TVh6eWF5YOSQAuf97ixeYrsBBGmP/sv/AAF5HV9vj5CHh2shAvv++fkv/vDZtY7C2gACACYAAAREBcAACgANAN9AOQwgDTkJDBkMKwxTDGsM4gwG7Q0BBgQWBCUEKA1IDVsNpw23DcYNCQECCAAMBg0HBQoLDQcADAwNDbgBrkAaAwQUAwMEAwIMBA0DDQIECgAHQA3ADdANAw27ASgACAACAbS2AAQEAAwMALgBWLQFjwoBCrgBAkASEAefB78HAwcaDz8CfwICAhkOugFMAUgAGCtOEORxEPZdTfRdPP08AD8/EPQ8/V08ARESORI5OQAREjkSOYcFLisEfRDEDw8PsQYCQ1RYQAstDD0MTQzNDN0MBQBdWTEwAV1dAF0rIREhNQEzETMVIxEBEQECfv2oAnzstrb+8P6vASf2A6P8Xvf+2QIeAfX+CwABAJkAAAXDBboACwGRQBoIBgESEgoKBQMCAwQGBgcJCgkICgUJCAkKCLgBt0ArBwYUBwcGAwQEIAUKFAUFCgoJAwMGCgMJAwgLBgYHBQQEAgECAAsLCAcIBLgCZLIFSAi4AmRAEgcaIA0wDQINAgsgASAAMAACALgCi7MMMWMYK04Q9F08Tf08GU4QXfYYTe307QA/PDwQPD88PBA8GRI5LwEREhc5ABIXOYcFLhgrBH0QxIcFLhgrCH0QxIcIEDwIxAMIEDwIPLEGAkNUWLUJIAsNNAO4/8qyCCc0ACsrWTEwAENYQBkmBicJkASYBqAEsATABAeEBqgE6AT2BQQJuP/gszdSNAm4/8BAJDdSNCUGPQp0A4YDmQOZCZoKqgO6A8kDCsED0AP8CgM9CkIDAnJxXSsBK3FdWUNcWLkABv/osxILPwa4/+hAEw8LPwQwDRY/BDAMFD8EIAsSPwO4/9CzDxk/A7j/0LMOFz8DuP/Qsw0WPwO4/9CzDBQ/A7j/0LMLEj8DuP/Qsg4TPwArKysrKysBKysrKytZAV0zESERASEBASEBBxGZASgCVgGO/dgCRv6B/m3wBbr9dQKL/cX8gQKw9f5FAAIAV//nBCoFwAAXACMAvUA7agt1CIcIlxmnBacIqQ6qE7kOthG9E8ARzxMNFQU2EUQQeha1AtIQ0BQHuwDPAAIABAEHGBIQGyAbAhu4/8BAHhIWNBumDw0fIS8hAiFAEhY0IaY/CQFACdAJ/wkDCbgBT7OvAQEBuAEhQCIfBC8EAgRAEhY0BKYVBQHYAHce2E8MAQwaJRjYEhkk08IYK04Q9E3tThD2cU3t9O0AP/0rcfRd9l1x7StxP+0rcQEREjkAERI5XTEwAV0AXQEFJiYjIgYHNjMyEhUUACMiABEQADMyFgEUFjMyNjU0JiMiBgQP/vAKVENZexBpnLD7/vjP3v7iASrup9v9oX5RTmhwVFFwBFMeVFCg/Xz+9NTh/vABWQGJAZMBZLv86YmVeouPhX8AAQC7BKgCgAXTAAMAQ0ATASASFTQBIB4kNFADAQADEAMCA7gBWUAJAQAD5QAQAgECuAJhtwFuABkE5nwYK04Q9E307V0Q7QA/7V1xMTABKysTEyEBu4oBO/7tBKgBK/7VAAABAAAAAuZnAAAAAF8PPPUIOQgAAAAAAKLjPB0AAAAAudW1E/r6/P0QAAgVAAEACQABAAEAAAAAAAEAAAc+/k4AQxAA+vr+JhAAAAEAAAAAAAAAAAAAAAAAAAA4BgABAAAAAAACOQAAAjkAAAKqAHMFxwCWAjkAkwI5AJMEcwBBAqoAHwTjACwEcwBJAx0AhwKqABgFxwCYBOMAUgcdAH4COQCMBOMAkQTjAI0COf/9BOMAkgTjAIcFxwCTBHMAMARzADMEcwBTBHMAVgRzAE0EcwCiBHMAQQKqAGsCqgBDAqoAyQI5AJMEcwBBBccAAATjAFQGOQBiBVYAlQTjAJ0FxwCWBVb//wVWAEoGqgCRBVYAlQXHAGEEcwAMBHP/4AMdABwEcwBXBHMAWwRzACYFxwCZBHMAVwKqALsAAAAAAAAAbAAAAGwAAABsAAAAbAAAALoAAAIiAAACxAAAAzAAAAU8AAAGXAAABvIAAAkAAAAJ5AAACuQAAAziAAAN0gAAD4YAABAOAAAQ+AAAEeAAABI2AAATNgAAFDIAABUSAAAYWAAAGf4AABucAAAcoAAAHfgAAB56AAAexgAAH1AAAB/aAAAgNAAAIHIAACG4AAAjMgAAJCIAACVoAAAmJgAAJoIAACfmAAApIAAAK4AAAC3UAAAupAAAL94AADG0AAAzTAAANIAAADU8AAA2rgAAN8gAADmSAAA6xgAAOygAAQAAADgA8gA8AG8ABgACABAALwBVAAAGThBvAAMAAgAAABQA9gABAAAAAAAAABAAAAABAAAAAAABABMAEAABAAAAAAACAAcAIwABAAAAAAADAAgAKgABAAAAAAAEABMAMgABAAAAAAAFAAwARQABAAAAAAAGAAAAUQABAAAAAAAHAAcAUQABAAAAAAAIAAcAWAABAAAAAAAJAAcAXwADAAEECQAAACAAZgADAAEECQABACYAhgADAAEECQACAA4ArAADAAEECQADABAAugADAAEECQAEACYAygADAAEECQAFABgA8AADAAEECQAGAAABCAADAAEECQAHAA4BCAADAAEECQAIAA4BFgADAAEECQAJAA4BJE9yaWdpbmFsIGxpY2VuY2VFRkpFRkUrQXJpYWwtQm9sZE1UVW5rbm93bnVuaXF1ZUlERUZKRUZFK0FyaWFsLUJvbGRNVFZlcnNpb24gMC4xMVVua25vd25Vbmtub3duVW5rbm93bgBPAHIAaQBnAGkAbgBhAGwAIABsAGkAYwBlAG4AYwBlAEUARgBKAEUARgBFACsAQQByAGkAYQBsAC0AQgBvAGwAZABNAFQAVQBuAGsAbgBvAHcAbgB1AG4AaQBxAHUAZQBJAEQARQBGAEoARQBGAEUAKwBBAHIAaQBhAGwALQBCAG8AbABkAE0AVABWAGUAcgBzAGkAbwBuACAAMAAuADEAMQBVAG4AawBuAG8AdwBuAFUAbgBrAG4AbwB3AG4AVQBuAGsAbgBvAHcAbgADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAvgBUA5oADwEBAB//wAOYsxAUMkC4A5mzDxMyQEEQA5UAUAOVAAIAsANNAMADTQACAG8DkQB/A5EAAv/AA0uyLTEyuf/AA0uzCg4yEEEQA4sAIAOLAIADiwADAKADiwABACADiwBAA4sAAv/AA4uzExYyQLgDg7IPETK5/8ADe7IwNDK5/8ADe7MQGDJQQRQDeAABA2UDbgAjAB8DfgNuAB4AHwNjA24AHQAfA2IDZAANAB//wANAsw8QMoBBEAM/AAEDPwMVACkAHwNBAxYAMgAfA0QDGgAbAB//wAN1sg4RMrn/wAN1sigqMkEKA0MDGAAyAB8DDwMNADQAHwMIAweyMh8guwNAAAEAQAOIswkLMkC4A4iyEBUyvQOFAwcAFAAfA4ADB7IXHw+9AwoALwMKAAL/wANUswkNMpBBDANUAKADVAACAB8DbgABAJ8DbgABAEADbrIJCzJBCgNFAxwAFgAfA2sDHQAVAB8DRgMeshUfwLsDkwABAEADkrMJDTJAuAM+sggzQLgDPrMNDjLAQQkDPgABALADjgDAA44AAv/AA5CzJjgyAEEmAygAMAMoAAIAIAN/ADADfwACABADigAwA4oAUAOKAG8DigB/A4oAnwOKAAYAAAOJADADiQACAC8DegBwA3cAkAN3AJ8DegAE/8ADFbIPEDK5/8ADFbIkKDK5AxkDGLIyHxC7AxoAAf/AAxqzCQ4yQLgDGLISEzK5/8ADGLMMDjI/vQNzAE8DcwACAEADdLMXGDJvuwMqAAEAQAMssxgbMkC4A3CyCQwyvQMXAxYAMgAf/8ADFrIOETK9AxwDHgAWAB8DHQMeshUfsEEfAx4AAQAPAx8AAQLKAtAAFQAfAtMC1QANAB8CzwLQAA0AHwLLAtAADQAfAs0C0AANAB8CzgLQAA0AH//AAtCzCQwyQLgC0rMJDDLgQRwC5QABAF8C3QCfAuUAAgK7AsMAMAAfAtoCuAAyAB8C2QK5AD8AHwLYArgAZAAfArkCuAAzAB8CurIhyB+4ArizIcgfQLgDm7INFjK5/8ACw7IrLzK5/8ACw7IfJTK5/8ACw7IXGzK5/8ACw7ISFjJBJQLCAsEAHAAfAtcCwQAkAB8CwQLAACIAHwK/AsAAGAAfAsACdADIAB8CtQI1ADsAHwK0AjUAOwAfAsQCvAAeAB8CtwK2ADgAHwKzsg7IH7gCsLIHyB+4Aq+yBsgfuAKusgDIH7gCr7JQLx+8Aq4CqwAaAB8CrbImGh+4AqizJiQfD7sCNQABAqUCdLIdHxJBCgKhAVgB9AAfAqAA2AH0AB8AEgKisjfIH7gCkLK8IB+5ApACkEAYN0AlQC1ApgMwJTAtMKYDICUgLSA3IKYgQRACjgAFAJ8CiwABAosCiwA3ACACiQAwAokAQAKJAJACibIEN7BB/QJ0AMACdAACAIACdACgAnQAAgBgAnQAcAJ0AAIAAAJ0ABACdAACAIACdADwAnQAAgA/AoUATwKFAAIAkAJ+AJACfwCQAoAAkAKBAAQAkAJ6AJACewCQAnwAkAJ9AAQAkAJ0AJACdQCQAncAAwBwAn4AcAJ/AHACgABwAoEABABwAnoAcAJ7AHACfABwAn0ABABwAnQAcAJ1AHACdwADAGACfgBgAn8AYAKAAGACgQAEAGACegBgAnsAYAJ8AGACfQAEAGACdABgAnUAYAJ3AAMAUAJ+AFACfwBQAoAAUAKBAAQAUAJ6AFACewBQAnwAUAJ9AAQAUAJ0AFACdQBQAncAAwBAAn4AQAJ/AEACgABAAoEABABAAnoAQAJ7AEACfABAAn0ABABAAnQAQAJ1AEACdwADADACfgAwAn8AMAKAADACgQAEADACegAwAnsAMAJ8ADACfQAEADACdAAwAnUAMAJ3AAMAIAJ+ACACfwAgAoAAIAKBAAQAIAJ6ACACewAgAnwAIAJ9AAQAIAJ0ACACdQAgAncAAwAQAn4AEAJ/ABACgAAQAoEABAAQAnoAEAJ7ABACfAAQAn0ABAAQAnQAEAJ1ABACdwADAOACfgDgAn8A4AKAAOACgQAEAOACegDgAnsA4AJ8AOACfQAEAOACdADgAnUA4AJ3sQPQQcUCfgDQAn8A0AKAANACgQAEANACegDQAnsA0AJ8ANACfQAEANACdADQAnUA0AJ3AAMAMAJ0AEACdAACAMACfgDAAn8AwAKAAMACgQAEAMACegDAAnsAwAJ8AMACfQAEAMACdADAAnUAwAJ3AAMAsAJ+ALACfwCwAoAAsAKBAAQAsAJ6ALACewCwAnwAsAJ9AAQAsAJ0ALACdQCwAncAAwCgAn4AoAJ/AKACgACgAoEABACgAnoAoAJ7AKACfACgAn0ABACgAnQAoAJ1AKACdwADAJACfgCQAn8AkAKAAJACgQAEAJACegCQAnsAkAJ8AJACfQAEAJACdACQAnUAkAJ3AAMAIAJ+ACACfwAgAoAAIAKBAAQAIAJ6ACACewAgAnwAIAJ9AAQAIAJ0ACACdQAgAncAAwKBAVgIAQAfAoABKQgBAB8CfwDsCAEAHwJ+ANgIAQAfAn0AsQgBAB8CfACmCAEAHwJ7AIIIAQAfAnoANwgBAB8CdwAmCAEAHwJ1ACAIAQAfAnQAHwgBsh83D0EWAjUATwI1AF8CNQBvAjUAnwI1AK8CNQC/AjUABwCvAjUAzwI1AN8CNQD/AjVAIgQPB08HnwevB78HBa8H4AcCDwZPBp8Grwa/BgWvBuAGAiBBGwINAAEAXwI1AAEAjwI1AAEAfwI1AO8CNQACAC8CNQA/AjUAAgA/AjQATwI0AAICNQI1AjQCNEAR7SDvKgHPKgG/KgGvKgGPKgFBCQJHAQQAHgAfAiAANwIBAB8BWEAMJj4f2CY+HzcmJz4fuAKOtuwXH7ImNh+4AbyyJjYfuAEpQCsmNh/sJjYfsSY2H6YmNh+CJjYfNyY2HzImNh8tJjYfJSY2Hx8mNh83JiofuAFYQCImPh/YJj4fvCY+HycmPh8hJj4fICY+HzcAFhYAAAASEQhAuQINAaazxQ0ACbgBvLInKB+4AbuyJzAfuAG4sidPH7gBt7InYh9BCQG2ACcBAQAfAbUAIAKrAB8Br7If5B+4Aa2yH+QfuAGssh+7H7gBqLIfNB+4AV2yJy4fuAFbsifNH0ENAVUAHwQBAB8BVAAfBAEAHwFTAB8CAQAfAVKyH1YfuAFRsh8pH7gBK7InJh9BDQEqACcBJQAfASkBWADkAB8BJQAfBAEAHwEksh/kH7gBI7IfOx+4ASKyHzkfQQ0BCAAnCAEAHwEGAC0BAQAfAQUAHwEBAB8BA7Mfux/vuQFYBAFACx/tH5Mf7B/kH+sfuAIBsh/ZILgEAbIfzyW4AVZACh+8LZ4fux9BH7JBCgFYBAEAHwCxAVgEAQAfALABWAQBtR+mJYkfm7kBWAElth+ZHy4fji24CAG1H40fKR+JuQFYBAGyH4IguAKrQBMfgB8wH3Qt5B9zH0ofYR9SH10luAKrsh9cH7wIAQAfAFkBWAKrth9QJYkfSR+4ASWyH0cluAQBQAsfRh95H0AfJx85ILwCqwAfADgBWAQBsh83LbwBJQAfADIBWAElth8sHzQfKiW4CAGyH1U3uAERQCoH8AeQB1sHQgc7ByMHIgceBx0HFAgSCBAIDggMCAoICAgGCAQIAggACBS4/+BAKwAAAQAUBhAAAAEABgQAAAEABBAAAAEAEAIAAAEAAgAAAAEAAAIBCAIASgCwEwNLAktTQgFLsMBjAEtiILD2UyO4AQpRWrAFI0IBsBJLAEtUQrA4K0u4B/9SsDcrS7AHUFtYsQEBjlmwOCuwAoi4AQBUWLgB/7EBAY6FG7ASQ1ixAQCFjRu5AAEBGYWNWVkAGBZ2Pxg/Ej4ROUZEPhE5RkQ+ETlGRD4ROUZEPhE5RmBEPhE5RmBEKysrKysrKysrKysYKysrKysrKysrKxgrHbCWS1NYsKodWbAyS1NYsP8dWUuwgVMgXFi5Ag8CDUVEuQIOAg1FRFlYuQRwAg9FUli5Ag8EcERZWUuw5FMgXFi5ACACDkVEuQAnAg5FRFlYuQhCACBFUli5ACAIQkRZWUu4ASVTIFxYuQAmAg9FRLkAIQIPRURZWLkKDQAmRVJYuQAmCg1EWVlLuAQBUyBcWLHYIEVEsSAgRURZWLklAADYRVJYuQDYJQBEWVlLuAQBUyBcWLkBWAAmRUSxJiZFRFlYuSMgAVhFUli5AVgjIERZWUuwKVMgXFixHx9FRLEtH0VEWVi5AQ0AH0VSWLkAHwENRFlZS7AvUyBcWLEfH0VEsSUfRURZWLkBNQAfRVJYuQAfATVEWVlLuAMBUyBcWLEfH0VEsR8fRURZWLkUKAAfRVJYuQAfFChEWVkrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrKysrZUIrAbMxdX7DRWUjRWAjRWVgI0VgsIt2aBiwgGIgILF+dUVlI0UgsAMmYGJjaCCwAyZhZbB1I2VEsH4jRCCxMcNFZSNFILADJmBiY2ggsAMmYWWwwyNlRLAxI0SxAMNFVFixw0BlRLIxQDFFI2FEWbM/PFhBRWUjRWAjRWVgI0VgsIl2aBiwgGIgILFYPEVlI0UgsAMmYGJjaCCwAyZhZbA8I2VEsFgjRCCxP0FFZSNFILADJmBiY2ggsAMmYWWwQSNlRLA/I0SxAEFFVFixQUBlRLI/QD9FI2FEWUVpU0IBS1BYsQgAQllDXFixCABCWbMCCwoSQ1hgGyFZQhYQcD6wEkNYuTshGH4bugQAAagACytZsAwjQrANI0KwEkNYuS1BLUEbugQABAAACytZsA4jQrAPI0KwEkNYuRh+OyEbugGoBAAACytZsBAjQrARI0IAKysrKysrKysAsBJDWEuwNVFLsCFTWlixJiZFsEBhRFlZKysrKysrKysrKysrKysrKysrK3Nzc3NzRbBAYUQYAEVpREVpRHNzc3Rzc3N0c3RzdCsrKysrKysrKysrKwBzc3Nzc3Nzc3Nzc3Nzc3Nzc3Nzc3NzdHR0dHR0dHR0dHR0dHR0dHR0dHR0dXV1c3R1dXV1K3MAAEuwKlNLsDZRWlixBwdFsEBgRFkAS7AuU0uwNlFaWLEDA0WwQGBEsQkJRbj/wGBEWStFaUQBdABzc3MrRWlEKwErQ1xYQAoABgAHAqAGoAcCuf/AAnSzGh0yb70CdwB/AncAAv/AAneyLzEyuf/AAnezIiUyQLgCdLMvNTJAuAJ0sygqMkC4AnSyGiEyuP/AszcaHTK4/8CzJRodMrj/wEARLRodMpAlkC2QN6AloC2gNwa4/8C2phodMh+mH7gCjrIvpgMAdCtzKysrKysrKyt0K3N0WQArK0NcWLn/wAKhshwdMrn/wAKgshwdMisrWStzASsrKysAKysrKysrKysrKysrKysrKysrASsrKysrKytzdCsrKysrKysrc3MrKysrKytzK3MrKyt0Kysrc3Nzc3Mrc3MrKytzACsrKytzdHMrcysrKyt1KysrKysrKyt1KysrKytzKysrK3N0dSsrc3NzKysrKwA=);
+}
+body { font-family: test; }
+</style>
+</head>
+<body>A</body>
+</html> \ No newline at end of file
diff --git a/gfx/tests/crashtests/893572-1.html b/gfx/tests/crashtests/893572-1.html
new file mode 100644
index 0000000000..58807459ef
--- /dev/null
+++ b/gfx/tests/crashtests/893572-1.html
@@ -0,0 +1,11 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.strokeRect(1.7976931348623157e+308, 0.651, 8, 34.323262543409996);
+o1.strokeRect(34.323262543409996, 1.7976931348623157e+308, 0.651, 8);
+o1.strokeRect(8, 34.323262543409996, 1.7976931348623157e+308, 0.651);
+o1.strokeRect(0.651, 8, 34.323262543409996, 1.7976931348623157e+308);
+
+</script>
diff --git a/gfx/tests/crashtests/893572-2.html b/gfx/tests/crashtests/893572-2.html
new file mode 100644
index 0000000000..354c15613c
--- /dev/null
+++ b/gfx/tests/crashtests/893572-2.html
@@ -0,0 +1,30 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.rect(1.7976931348623157e+308, 0.651, 8, 34.323262543409996);
+o1.stroke()
+
+o1.rect(-1.7976931348623157e+308, 0.651, 8, 34.323262543409996);
+o1.stroke()
+
+o1.rect(34.323262543409996, 1.7976931348623157e+308, 0.651, 8);
+o1.stroke()
+
+o1.rect(34.323262543409996, -1.7976931348623157e+308, 0.651, 8);
+o1.stroke()
+
+o1.rect(8, 34.323262543409996, 1.7976931348623157e+308, 0.651);
+o1.stroke()
+
+o1.rect(8, 34.323262543409996, -1.7976931348623157e+308, 0.651);
+o1.stroke()
+
+o1.rect(0.651, 8, 34.323262543409996, 1.7976931348623157e+308);
+o1.stroke()
+
+o1.rect(0.651, 8, 34.323262543409996, -1.7976931348623157e+308);
+o1.stroke()
+
+</script>
diff --git a/gfx/tests/crashtests/893572-3.html b/gfx/tests/crashtests/893572-3.html
new file mode 100644
index 0000000000..d40f3c997a
--- /dev/null
+++ b/gfx/tests/crashtests/893572-3.html
@@ -0,0 +1,44 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(1.7976931348623157e+308,34.323262543409996);
+o1.arcTo(1.7976931348623157e+308, 150, 20, 150, 70);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(-1.7976931348623157e+308,34.323262543409996);
+o1.arcTo(70, 1.7976931348623157e+308, 150, 20, 150);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, 1.7976931348623157e+308);
+o1.arcTo(150, 70, 1.7976931348623157e+308, 150, 20);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, -1.7976931348623157e+308);
+o1.arcTo(20, 150, 70,1.7976931348623157e+308, 150);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(20, 20);
+o1.lineTo(100, 20);
+o1.arcTo(150, 20, 150, 70, 1.7976931348623157e+308);
+o1.lineTo(150, 120);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(20, 20);
+o1.lineTo(100, 20);
+o1.arcTo(150, 20, 150, 70, -1.7976931348623157e+308);
+o1.lineTo(150, 120);
+o1.stroke();
+
+</script>
diff --git a/gfx/tests/crashtests/893572-4.html b/gfx/tests/crashtests/893572-4.html
new file mode 100644
index 0000000000..b6d8212e7a
--- /dev/null
+++ b/gfx/tests/crashtests/893572-4.html
@@ -0,0 +1,38 @@
+<script>
+o0 = document.createElement('canvas');
+(document.body || document.documentElement).appendChild(o0);
+o1 = o0.getContext('2d');
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(1.7976931348623157e+308,34.323262543409996);
+o1.lineTo(1.7976931348623157e+308,44.323262543409996);
+o1.lineTo(10.0,44.323262543409996);
+o1.lineTo(8,34.323262543409996);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, 1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, 1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, 10.0);
+o1.lineTo(34.323262543409996, 8);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(8,34.323262543409996);
+o1.lineTo(-1.7976931348623157e+308,34.323262543409996);
+o1.lineTo(-1.7976931348623157e+308,44.323262543409996);
+o1.lineTo(10.0,44.323262543409996);
+o1.lineTo(8,34.323262543409996);
+o1.stroke();
+
+o1.beginPath();
+o1.moveTo(34.323262543409996, 8);
+o1.lineTo(34.323262543409996, -1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, -1.7976931348623157e+308);
+o1.lineTo(44.323262543409996, 10.0);
+o1.lineTo(34.323262543409996, 8);
+o1.stroke();
+
+</script>
diff --git a/gfx/tests/crashtests/895233.html b/gfx/tests/crashtests/895233.html
new file mode 100644
index 0000000000..806a933256
--- /dev/null
+++ b/gfx/tests/crashtests/895233.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+
+function boom()
+{
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.isPointInPath(7.5, 900);
+ ctx.setTransform(44.333333333333336, 61, 256.3333333333333, 135.8, 810, 200);
+ ctx.save();
+ ctx.bezierCurveTo(63, 91, 520, 83, 213.66666666666666, 384);
+ ctx.closePath();
+ ctx.bezierCurveTo(0.5, 83.16666666666667, 209, 276, 3.6, 453.5);
+ ctx.stroke();
+ ctx.restore();
+ ctx.isPointInStroke(0, 0);
+ ctx.setTransform(445, 33, 0, 403.5, 175.2, 49.4);
+ ctx.rotate(83.125);
+ ctx.transform(2097153, 477.5, 34.888888888888886, 63, 9.5, 914);
+ ctx.isPointInStroke(0, 0);
+}
+
+</script>
+</head>
+<body onload="boom();"></body>
+</html>
diff --git a/gfx/tests/crashtests/914457-1.html b/gfx/tests/crashtests/914457-1.html
new file mode 100644
index 0000000000..b74d4831bc
--- /dev/null
+++ b/gfx/tests/crashtests/914457-1.html
@@ -0,0 +1,9 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<html style="transform: skewY(0.08turn); overflow: clip; position: absolute;">
+ <body style="border-bottom: thick solid; transform: skewX(30grad);">
+ <table style="visibility: collapse; display: list-item;"></table>
+ </body>
+</html>
+
diff --git a/gfx/tests/crashtests/934729.html b/gfx/tests/crashtests/934729.html
new file mode 100644
index 0000000000..a9e39affe0
--- /dev/null
+++ b/gfx/tests/crashtests/934729.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<meta charset="UTF-8">
+<body>
+<span style="position: sticky; display: -moz-inline-box; transform-style: preserve-3d;">A</span>
+</body>
+</html>
diff --git a/gfx/tests/crashtests/944579.html b/gfx/tests/crashtests/944579.html
new file mode 100644
index 0000000000..3667f6024e
--- /dev/null
+++ b/gfx/tests/crashtests/944579.html
@@ -0,0 +1 @@
+><svg><filter width=60cm height=190.339893225in id=morphology><feMorphology operator=dilate width=140 radius=10><img src=944579.png style="-moz-filter: url(#morphology); filter: url(#morphology);">
diff --git a/gfx/tests/crashtests/944579.png b/gfx/tests/crashtests/944579.png
new file mode 100644
index 0000000000..68641b7677
--- /dev/null
+++ b/gfx/tests/crashtests/944579.png
Binary files differ
diff --git a/gfx/tests/crashtests/944579.svg b/gfx/tests/crashtests/944579.svg
new file mode 100644
index 0000000000..cefe73c9fe
--- /dev/null
+++ b/gfx/tests/crashtests/944579.svg
@@ -0,0 +1,26 @@
+<html><head></head><body><svg xmlns="http://www.w3.org/2000/svg" width="500">
+
+<filter>
+ <feMorphology></feMorphology>
+</filter>
+<g><rect></rect>
+</g>
+
+<filter>
+<feMorphology></feMorphology>
+</filter>
+<g>
+ <rect></rect>
+</g>
+
+<filter id="f3" primitiveUnits="objectBoundingBox">
+ <feMorphology operator="dilate" radius="32542"></feMorphology>
+</filter>
+<g filter="url(#f3)"><rect width="4294967217ex" height="100" x="29%"></rect>
+</g>
+
+<filter></filter>
+<g></g>
+
+</svg>
+</body><style>></style></html><!-- --> \ No newline at end of file
diff --git a/gfx/tests/crashtests/950000.html b/gfx/tests/crashtests/950000.html
new file mode 100644
index 0000000000..f2c35f43ff
--- /dev/null
+++ b/gfx/tests/crashtests/950000.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style id="s">
+
+@font-face {
+ font-family: x;
+ src: url(bogus-font.ttf);
+}
+
+@font-face {
+ font-family: y;
+ src: url(bogus-font.ttf);
+}
+
+</style>
+
+<script>
+
+function boom()
+{
+ var canvas = document.createElement('canvas');
+ var ctx = canvas.getContext('2d');
+ ctx.font = 'normal 20px x';
+
+ document.getElementById("s").remove();
+
+ setTimeout(function() {
+ ctx.measureText("A");
+ }, 50);
+}
+
+window.addEventListener("DOMContentLoaded", boom)
+
+</script>
+</head>
+
+<body><div style="font-family: y;">y</div></body>
+</html>
diff --git a/gfx/tests/crashtests/951893.xhtml b/gfx/tests/crashtests/951893.xhtml
new file mode 100644
index 0000000000..961917c39d
--- /dev/null
+++ b/gfx/tests/crashtests/951893.xhtml
@@ -0,0 +1,7 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body style="display: flex; position: absolute; transform: matrix3d(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16); overflow-y: hidden;">
+
+<video></video><video></video><video></video><track style="transform: rotate(45deg) scale(2, 1); position: absolute;"><video></video></track>
+
+</body>
+</html>
diff --git a/gfx/tests/crashtests/987013.html b/gfx/tests/crashtests/987013.html
new file mode 100644
index 0000000000..b99a7a1dc1
--- /dev/null
+++ b/gfx/tests/crashtests/987013.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html style="transform: scale3d(4, 2454, 4);"><body style="display: inline; outline: 722429773px dashed green;"></body></html>
diff --git a/gfx/tests/crashtests/PigLatin_Plane15.ttf b/gfx/tests/crashtests/PigLatin_Plane15.ttf
new file mode 100644
index 0000000000..ab83296031
--- /dev/null
+++ b/gfx/tests/crashtests/PigLatin_Plane15.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/Prototype.ttf b/gfx/tests/crashtests/Prototype.ttf
new file mode 100644
index 0000000000..c70bf00f47
--- /dev/null
+++ b/gfx/tests/crashtests/Prototype.ttf
Binary files differ
diff --git a/gfx/tests/crashtests/balinese-letter-spacing.html b/gfx/tests/crashtests/balinese-letter-spacing.html
new file mode 100644
index 0000000000..90a2c06a02
--- /dev/null
+++ b/gfx/tests/crashtests/balinese-letter-spacing.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<html><body style="letter-spacing: 300px"><div>&#x6978;</div></body></html>
diff --git a/gfx/tests/crashtests/crashtests.list b/gfx/tests/crashtests/crashtests.list
new file mode 100644
index 0000000000..6fffd8241f
--- /dev/null
+++ b/gfx/tests/crashtests/crashtests.list
@@ -0,0 +1,203 @@
+load 122875-1.html
+load 156882-1.html
+load 157320-1.html
+load 199379-1.html
+load 206561-1.html
+load 248518-1.html
+load 306649-1.xml
+load 306902-1.xml
+load 333861-1.html
+load 334735-1.html
+load 345576-1.html
+load 345629-1.html
+load 348462-1.html
+load 348462-2.html
+load 366643.html
+load 369688-1.html
+load 369947-1.html
+load 372094-1.xhtml
+load 376627-1.html
+load 377231-1.html
+load 377232-1.xhtml
+load 377461-1.xhtml
+load 383473-1.html
+load 383872-1.svg
+load 385228-1.svg
+skip load 385228-2.svg # bug 523255 / bug 385228
+load 385289-1.xhtml
+load 385417-1.html
+load 385417-2.html
+load 385423-1.html
+load 385423-2.html
+load 385719-1.html
+load 389326-1.html
+load 390476.html
+load 393746-1.xhtml
+load 393749-1.html
+load 393822-1.html
+load 394384-1.html
+load 394246-1.html
+load 394246-2.html
+skip-if(Android) load 394751.xhtml # bug 922976
+load 395335-1.xhtml
+load 395458-1.html
+load 396321-1.svg
+load 398042-1.xhtml
+load 398042-2.xhtml
+load 402307-1.html
+load 403352.html
+load 403464-1.html
+load 404112-1.html
+load 404112-2.html
+load 405268-1.xhtml
+load 407761-1.html
+load 407842.html
+load 408754-1.html
+load 410728-1.xml
+load 416637-1.html
+load 419095-1.html
+load 419255-1.html
+load 420945-1.html
+load 420962-1.html
+load 421393-1.html
+load 421813-1.html
+load 423110-1.xhtml
+load 423270-1.html
+load 428633.html
+load 429899-1.html
+load 441360.html
+load 445711.html
+load 463307-1.html
+load 467703-1.xhtml
+load 467873-1.html
+load 470418-1.html
+load 474410-1.html
+load 487549-1.html
+load 487724-1.html
+load 490777-1.html
+load 516512-1.html
+load 532726-1.html
+load 538065-1.html
+load 546870-1.html
+load 557348-1.html
+load 563740-1.html
+load 580100-1.html
+load 580212-1.html
+load 580233-1.html
+load 580719-1.html
+load 593526.html
+load chrome://reftest/content/crashtests/gfx/tests/crashtests/593526.xhtml
+load 594654-1.xhtml
+load 595042-1.html
+load 595727-1.html
+load 624198.xhtml
+load 633322-1.html
+load 633453-1.html
+load 662467-1.html
+load 665218.html
+load 675550-1.html
+load 686190-1.html
+load 691330.svg
+load 691581-1.html
+load 693143-1.html
+load 696936-1.html
+load 699563-1.html
+load 710149-1.html
+load 746491.html
+load 746495.html
+load 746497.html
+load 746844.html
+load 746847.html
+load 746849.html
+load 746866.html
+load 747132.html
+load 747302.html
+load 766422-1.html
+load 766422-2.html
+load 766452-1.html
+load 766452-2.html
+load 768079-1.html
+load 783041-1.html
+load 783041-2.html
+load 783041-3.html
+load 783041-4.html
+load 798853.html # bug 868792
+load 805760-1.html
+load 815489.html
+load 812826.html
+load 836225-1.html
+load 839745-1.html
+load 856784-1.html
+load 893572-1.html
+load 893572-2.html
+load 893572-3.html
+load 893572-4.html
+load 895233.html
+pref(layers.force-active,true) load 914457-1.html
+load 934729.html
+load 944579.svg
+load 944579.html
+pref(security.fileuri.strict_origin_policy,false) load 950000.html
+load 951893.xhtml
+load 987013.html
+pref(layers.force-active,true) load 1008983.html
+load 1011218.html
+load 1034403-1.html
+load 1056516.html
+load 1205900.html
+load 1134549-1.svg
+load balinese-letter-spacing.html
+load 1216832-1.html
+load 1221304.html
+load 1225125-1.html
+load 1228127.html
+load 1229972.html
+load 1242811.html
+load 1242822.html
+load 1248222.html
+load 1278305.html
+load 1308394.html
+skip-if(geckoview&&webrender) load 1317403-1.html # bug 1331533, 1630774
+skip-if(geckoview&&webrender) load 1325159-1.html # bug 1630774
+skip-if(geckoview&&webrender) load 1331683.html # bug 1630774
+skip-if(Android) pref(dom.disable_open_during_load,false) load 1343666.html
+load 1346601-1.html
+skip-if(geckoview&&webrender) load 1408078-1.html # bug 1630774
+load 1464243.html
+load 1467847-1.html
+load 1468020.html
+load 1470437.html
+load 1470440.html
+load 1478035.html
+load 1490704-1.html
+load 1501518.html
+load 1503986-1.html
+load 1505426-1.html
+load 1508811.html
+load 1508822.html
+load 1509099.html
+load 1513133.html
+load 1496194.html
+load 1505934-1.html
+load 1509123.html
+load 1494062-blob-image-wraplist-clip.html
+load texture-allocator-zero-region.html
+load 1524418.html
+pref(layout.css.individual-transform.enabled,true) load 1529149.html
+load 1541113.html
+pref(layout.css.individual-transform.enabled,true) load 1547169.html
+load 1535657.html
+load 1566206.html
+load 1615141.html
+load 1615091.html
+load 1620125.html
+load 1640401-1.html
+load 1647862.html
+load 1647940.html
+load 1650989-very-large-mask.html
+load 1650990.html
+skip-if(!webrender||AddressSanitizer) load 1652750-deep-scene-stack.html
+load 1651882.html
+skip-if(!webrender) load 1678938-1.html
+load 1679477-1.html
+skip-if(Android) pref(dom.disable_open_during_load,false) load 1685009-1.html
diff --git a/gfx/tests/crashtests/empty.html b/gfx/tests/crashtests/empty.html
new file mode 100644
index 0000000000..9daeafb986
--- /dev/null
+++ b/gfx/tests/crashtests/empty.html
@@ -0,0 +1 @@
+test
diff --git a/gfx/tests/crashtests/texture-allocator-zero-region.html b/gfx/tests/crashtests/texture-allocator-zero-region.html
new file mode 100644
index 0000000000..1671053a4b
--- /dev/null
+++ b/gfx/tests/crashtests/texture-allocator-zero-region.html
@@ -0,0 +1,9 @@
+<style>
+* {
+ column-width: 1px;
+ -webkit-filter: hue-rotate(1deg);
+ outline: 3px solid;
+}
+</style>
+<form style="overflow:hidden">
+<h3>
diff --git a/gfx/tests/gtest/MockWidget.cpp b/gfx/tests/gtest/MockWidget.cpp
new file mode 100644
index 0000000000..696845641e
--- /dev/null
+++ b/gfx/tests/gtest/MockWidget.cpp
@@ -0,0 +1,8 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "MockWidget.h"
+
+NS_IMPL_ISUPPORTS_INHERITED0(MockWidget, nsBaseWidget)
diff --git a/gfx/tests/gtest/MockWidget.h b/gfx/tests/gtest/MockWidget.h
new file mode 100644
index 0000000000..ae9a529a4c
--- /dev/null
+++ b/gfx/tests/gtest/MockWidget.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GTEST_MOCKWIDGET_H
+#define GTEST_MOCKWIDGET_H
+
+#include "mozilla/gfx/Point.h"
+#include "mozilla/widget/InProcessCompositorWidget.h"
+#include "nsBaseWidget.h"
+#include "GLContext.h"
+#include "GLContextProvider.h"
+
+using mozilla::gl::CreateContextFlags;
+using mozilla::gl::GLContext;
+using mozilla::gl::GLContextProvider;
+
+using mozilla::gfx::IntSize;
+
+class MockWidget : public nsBaseWidget {
+ public:
+ MockWidget() : mCompWidth(0), mCompHeight(0) {}
+ MockWidget(int aWidth, int aHeight)
+ : mCompWidth(aWidth), mCompHeight(aHeight) {}
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual LayoutDeviceIntRect GetClientBounds() override {
+ return LayoutDeviceIntRect(0, 0, mCompWidth, mCompHeight);
+ }
+ virtual LayoutDeviceIntRect GetBounds() override { return GetClientBounds(); }
+
+ void* GetNativeData(uint32_t aDataType) override {
+ if (aDataType == NS_NATIVE_OPENGL_CONTEXT) {
+ nsCString discardFailureId;
+ RefPtr<GLContext> context = GLContextProvider::CreateHeadless(
+ {CreateContextFlags::REQUIRE_COMPAT_PROFILE}, &discardFailureId);
+ if (!context) {
+ return nullptr;
+ }
+ if (!context->CreateOffscreenDefaultFb({mCompWidth, mCompHeight})) {
+ return nullptr;
+ }
+ return context.forget().take();
+ }
+ return nullptr;
+ }
+
+ virtual nsresult Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
+ const LayoutDeviceIntRect& aRect,
+ nsWidgetInitData* aInitData = nullptr) override {
+ return NS_OK;
+ }
+ virtual nsresult Create(nsIWidget* aParent, nsNativeWidget aNativeParent,
+ const DesktopIntRect& aRect,
+ nsWidgetInitData* aInitData = nullptr) override {
+ return NS_OK;
+ }
+ virtual void Show(bool aState) override {}
+ virtual bool IsVisible() const override { return true; }
+ virtual void Move(double aX, double aY) override {}
+ virtual void Resize(double aWidth, double aHeight, bool aRepaint) override {}
+ virtual void Resize(double aX, double aY, double aWidth, double aHeight,
+ bool aRepaint) override {}
+
+ virtual void Enable(bool aState) override {}
+ virtual bool IsEnabled() const override { return true; }
+ virtual void SetFocus(Raise, mozilla::dom::CallerType aCallerType) override {}
+ virtual nsresult ConfigureChildren(
+ const nsTArray<Configuration>& aConfigurations) override {
+ return NS_OK;
+ }
+ virtual void Invalidate(const LayoutDeviceIntRect& aRect) override {}
+ virtual nsresult SetTitle(const nsAString& title) override { return NS_OK; }
+ virtual LayoutDeviceIntPoint WidgetToScreenOffset() override {
+ return LayoutDeviceIntPoint(0, 0);
+ }
+ virtual nsresult DispatchEvent(mozilla::WidgetGUIEvent* aEvent,
+ nsEventStatus& aStatus) override {
+ return NS_OK;
+ }
+ virtual void SetInputContext(const InputContext& aContext,
+ const InputContextAction& aAction) override {}
+ virtual InputContext GetInputContext() override { abort(); }
+
+ private:
+ ~MockWidget() = default;
+
+ int mCompWidth;
+ int mCompHeight;
+};
+
+#endif
diff --git a/gfx/tests/gtest/PolygonTestUtils.cpp b/gfx/tests/gtest/PolygonTestUtils.cpp
new file mode 100644
index 0000000000..04a2f47498
--- /dev/null
+++ b/gfx/tests/gtest/PolygonTestUtils.cpp
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PolygonTestUtils.h"
+
+#include <cmath>
+
+#include "Point.h"
+#include "Triangle.h"
+
+typedef mozilla::gfx::Polygon MozPolygon;
+
+using mozilla::gfx::Point;
+using mozilla::gfx::Point4D;
+using mozilla::gfx::Triangle;
+
+namespace mozilla {
+namespace gfx {
+
+const float kEpsilon = 0.001f;
+
+// Compares two points while allowing some numerical inaccuracy.
+bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs) {
+ const auto d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon && std::abs(d.y) < kEpsilon &&
+ std::abs(d.z) < kEpsilon && std::abs(d.w) < kEpsilon;
+}
+
+bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs) {
+ const auto d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon && std::abs(d.y) < kEpsilon &&
+ std::abs(d.z) < kEpsilon;
+}
+
+bool FuzzyEquals(const Point& lhs, const Point& rhs) {
+ const auto d = lhs - rhs;
+
+ return std::abs(d.x) < kEpsilon && std::abs(d.y) < kEpsilon;
+}
+
+bool operator==(const Triangle& lhs, const Triangle& rhs) {
+ return FuzzyEquals(lhs.p1, rhs.p1) && FuzzyEquals(lhs.p2, rhs.p2) &&
+ FuzzyEquals(lhs.p3, rhs.p3);
+}
+
+// Compares the points of two polygons and ensures
+// that the points are in the same winding order.
+bool operator==(const MozPolygon& lhs, const MozPolygon& rhs) {
+ const auto& left = lhs.GetPoints();
+ const auto& right = rhs.GetPoints();
+
+ // Polygons do not have the same amount of points.
+ if (left.Length() != right.Length()) {
+ return false;
+ }
+
+ const size_t pointCount = left.Length();
+
+ // Find the first vertex of the first polygon from the second polygon.
+ // This assumes that the polygons do not contain duplicate vertices.
+ int start = -1;
+ for (size_t i = 0; i < pointCount; ++i) {
+ if (FuzzyEquals(left[0], right[i])) {
+ start = i;
+ break;
+ }
+ }
+
+ // Found at least one different vertex.
+ if (start == -1) {
+ return false;
+ }
+
+ // Verify that the polygons have the same points.
+ for (size_t i = 0; i < pointCount; ++i) {
+ size_t j = (start + i) % pointCount;
+
+ if (!FuzzyEquals(left[i], right[j])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+TEST(PolygonTestUtils, TestSanity)
+{
+ EXPECT_TRUE(FuzzyEquals(Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f)));
+
+ EXPECT_TRUE(FuzzyEquals(Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.00001f, 0.00001f, 0.00001f, 1.0f)));
+
+ EXPECT_TRUE(FuzzyEquals(Point4D(0.00001f, 0.00001f, 0.00001f, 1.0f),
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f)));
+
+ EXPECT_FALSE(FuzzyEquals(Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.01f, 0.01f, 0.01f, 1.0f)));
+
+ EXPECT_FALSE(FuzzyEquals(Point4D(0.01f, 0.01f, 0.01f, 1.0f),
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f)));
+
+ MozPolygon p1{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f)};
+
+ // Same points as above shifted forward by one position.
+ MozPolygon shifted{
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{Point4D(0.00001f, 0.00001f, 1.00001f, 1.0f),
+ Point4D(1.00001f, 0.00001f, 1.00001f, 1.0f),
+ Point4D(1.00001f, 1.00001f, 1.00001f, 1.0f),
+ Point4D(0.00001f, 1.00001f, 1.00001f, 1.0f)};
+
+ MozPolygon p3{
+ Point4D(0.01f, 0.01f, 1.01f, 1.0f), Point4D(1.01f, 0.01f, 1.01f, 1.0f),
+ Point4D(1.01f, 1.01f, 1.01f, 1.0f), Point4D(0.01f, 1.01f, 1.01f, 1.0f)};
+
+ // Trivial equals
+ EXPECT_TRUE(p1 == p1);
+ EXPECT_TRUE(p2 == p2);
+ EXPECT_TRUE(p3 == p3);
+ EXPECT_TRUE(shifted == shifted);
+
+ // Polygons with the same point order
+ EXPECT_TRUE(p1 == p2);
+ EXPECT_TRUE(p1 == shifted);
+
+ // Polygons containing different points
+ EXPECT_FALSE(p1 == p3);
+ EXPECT_FALSE(p2 == p3);
+ EXPECT_FALSE(shifted == p3);
+
+ const nsTArray<Triangle> t1{
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f))};
+
+ const nsTArray<Triangle> t2{
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))};
+
+ const nsTArray<Triangle> t3{Triangle(Point(0.00001f, 0.00001f),
+ Point(0.00001f, 1.00001f),
+ Point(1.00001f, 1.00001f))};
+
+ EXPECT_TRUE(t1[0] == t1[0]);
+ EXPECT_TRUE(t2[0] == t2[0]);
+ EXPECT_TRUE(t3[0] == t1[0]);
+
+ EXPECT_FALSE(t1[0] == t2[0]);
+ EXPECT_FALSE(t2[0] == t3[0]);
+
+ AssertArrayEQ(t1, t1);
+ AssertArrayEQ(t2, t2);
+}
diff --git a/gfx/tests/gtest/PolygonTestUtils.h b/gfx/tests/gtest/PolygonTestUtils.h
new file mode 100644
index 0000000000..e860b6a8d1
--- /dev/null
+++ b/gfx/tests/gtest/PolygonTestUtils.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_TEST_POLYGONUTILS_H
+#define GFX_TEST_POLYGONUTILS_H
+
+#include "gtest/gtest.h"
+
+#include "nsTArray.h"
+#include "Point.h"
+#include "Polygon.h"
+#include "Triangle.h"
+
+namespace mozilla {
+namespace gfx {
+
+bool FuzzyEquals(const Point4D& lhs, const Point4D& rhs);
+bool FuzzyEquals(const Point3D& lhs, const Point3D& rhs);
+bool FuzzyEquals(const Point& lhs, const Point& rhs);
+
+bool operator==(const Triangle& lhs, const Triangle& rhs);
+bool operator==(const Polygon& lhs, const Polygon& rhs);
+
+// Compares two arrays with the equality operator.
+template <typename T>
+void AssertArrayEQ(const nsTArray<T>& rhs, const nsTArray<T>& lhs) {
+ ASSERT_EQ(lhs.Length(), rhs.Length());
+
+ for (size_t i = 0; i < lhs.Length(); ++i) {
+ EXPECT_TRUE(lhs[i] == rhs[i]);
+ }
+}
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* GFX_TEST_POLYGONUTILS_H */
diff --git a/gfx/tests/gtest/TestArena.cpp b/gfx/tests/gtest/TestArena.cpp
new file mode 100644
index 0000000000..7f9aad6592
--- /dev/null
+++ b/gfx/tests/gtest/TestArena.cpp
@@ -0,0 +1,185 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/gfx/IterableArena.h"
+#include <string>
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+#ifdef A
+# undef A
+#endif
+
+#ifdef B
+# undef B
+#endif
+
+// to avoid having symbols that collide easily like A and B in the global
+// namespace
+namespace test_arena {
+
+class A;
+class B;
+
+class Base {
+ public:
+ virtual ~Base() = default;
+ virtual A* AsA() { return nullptr; }
+ virtual B* AsB() { return nullptr; }
+};
+
+static int sDtorItemA = 0;
+static int sDtorItemB = 0;
+
+class A : public Base {
+ public:
+ virtual A* AsA() override { return this; }
+
+ explicit A(uint64_t val) : mVal(val) {}
+ ~A() { ++sDtorItemA; }
+
+ uint64_t mVal;
+};
+
+class B : public Base {
+ public:
+ virtual B* AsB() override { return this; }
+
+ explicit B(const std::string& str) : mVal(str) {}
+ ~B() { ++sDtorItemB; }
+
+ std::string mVal;
+};
+
+struct BigStruct {
+ uint64_t mVal;
+ uint8_t data[120];
+
+ explicit BigStruct(uint64_t val) : mVal(val) {}
+};
+
+static void TestArenaAlloc(IterableArena::ArenaType aType) {
+ sDtorItemA = 0;
+ sDtorItemB = 0;
+ IterableArena arena(aType, 256);
+
+ // An empty arena has no items to iterate over.
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item) { iterations++; });
+ ASSERT_EQ(iterations, 0);
+ }
+
+ auto a1 = arena.Alloc<A>(42);
+ auto b1 = arena.Alloc<B>("Obladi oblada");
+ auto a2 = arena.Alloc<A>(1337);
+ auto b2 = arena.Alloc<B>("Yellow submarine");
+ auto b3 = arena.Alloc<B>("She's got a ticket to ride");
+
+ // Alloc returns a non-negative offset if the allocation succeeded.
+ ASSERT_TRUE(a1 >= 0);
+ ASSERT_TRUE(a2 >= 0);
+ ASSERT_TRUE(b1 >= 0);
+ ASSERT_TRUE(b2 >= 0);
+ ASSERT_TRUE(b3 >= 0);
+
+ ASSERT_TRUE(arena.GetStorage(a1) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(a2) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b1) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b2) != nullptr);
+ ASSERT_TRUE(arena.GetStorage(b3) != nullptr);
+
+ ASSERT_TRUE(((Base*)arena.GetStorage(a1))->AsA() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(a2))->AsA() != nullptr);
+
+ ASSERT_TRUE(((Base*)arena.GetStorage(b1))->AsB() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(b2))->AsB() != nullptr);
+ ASSERT_TRUE(((Base*)arena.GetStorage(b3))->AsB() != nullptr);
+
+ ASSERT_EQ(((Base*)arena.GetStorage(a1))->AsA()->mVal, (uint64_t)42);
+ ASSERT_EQ(((Base*)arena.GetStorage(a2))->AsA()->mVal, (uint64_t)1337);
+
+ ASSERT_EQ(((Base*)arena.GetStorage(b1))->AsB()->mVal,
+ std::string("Obladi oblada"));
+ ASSERT_EQ(((Base*)arena.GetStorage(b2))->AsB()->mVal,
+ std::string("Yellow submarine"));
+ ASSERT_EQ(((Base*)arena.GetStorage(b3))->AsB()->mVal,
+ std::string("She's got a ticket to ride"));
+
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item) { iterations++; });
+ ASSERT_EQ(iterations, 5);
+ }
+
+ // Typically, running the destructors of the elements in the arena will is
+ // done manually like this:
+ arena.ForEach([](void* item) { ((Base*)item)->~Base(); });
+ arena.Clear();
+ ASSERT_EQ(sDtorItemA, 2);
+ ASSERT_EQ(sDtorItemB, 3);
+
+ // An empty arena has no items to iterate over (we just cleared it).
+ {
+ int iterations = 0;
+ arena.ForEach([&](void* item) { iterations++; });
+ ASSERT_EQ(iterations, 0);
+ }
+}
+
+static void TestArenaLimit(IterableArena::ArenaType aType,
+ bool aShouldReachLimit) {
+ IterableArena arena(aType, 128);
+
+ // A non-growable arena should return a negative offset when running out
+ // of space, without crashing.
+ // We should not run out of space with a growable arena (unless the os is
+ // running out of memory but this isn't expected for this test).
+ bool reachedLimit = false;
+ for (int i = 0; i < 100; ++i) {
+ auto offset = arena.Alloc<A>(42);
+ if (offset < 0) {
+ reachedLimit = true;
+ break;
+ }
+ }
+ ASSERT_EQ(reachedLimit, aShouldReachLimit);
+}
+
+} // namespace test_arena
+
+using namespace test_arena;
+
+TEST(Moz2D, FixedArena)
+{
+ TestArenaAlloc(IterableArena::FIXED_SIZE);
+ TestArenaLimit(IterableArena::FIXED_SIZE, true);
+}
+
+TEST(Moz2D, GrowableArena)
+{
+ TestArenaAlloc(IterableArena::GROWABLE);
+ TestArenaLimit(IterableArena::GROWABLE, false);
+
+ IterableArena arena(IterableArena::GROWABLE, 16);
+ // sizeof(BigStruct) is more than twice the initial capacity, make sure that
+ // this doesn't blow everything up, since the arena doubles its storage size
+ // each time it grows (until it finds a size that fits).
+ auto a = arena.Alloc<BigStruct>(1);
+ auto b = arena.Alloc<BigStruct>(2);
+ auto c = arena.Alloc<BigStruct>(3);
+
+ // Offsets should also still point to the appropriate values after
+ // reallocation.
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(a))->mVal, (uint64_t)1);
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(b))->mVal, (uint64_t)2);
+ ASSERT_EQ(((BigStruct*)arena.GetStorage(c))->mVal, (uint64_t)3);
+
+ arena.Clear();
+}
diff --git a/gfx/tests/gtest/TestArrayView.cpp b/gfx/tests/gtest/TestArrayView.cpp
new file mode 100644
index 0000000000..209f286845
--- /dev/null
+++ b/gfx/tests/gtest/TestArrayView.cpp
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <limits>
+
+#include "gtest/gtest.h"
+
+#include "mozilla/ArrayView.h"
+
+using namespace mozilla::gfx;
+
+TEST(Gfx, ArrayView)
+{
+ nsTArray<int> p = {5, 6};
+ ArrayView<int> pv(p);
+ ASSERT_EQ(pv[1], 6);
+ ASSERT_EQ(*pv.Data(), 5);
+}
diff --git a/gfx/tests/gtest/TestBSPTree.cpp b/gfx/tests/gtest/TestBSPTree.cpp
new file mode 100644
index 0000000000..b5c6cda5dd
--- /dev/null
+++ b/gfx/tests/gtest/TestBSPTree.cpp
@@ -0,0 +1,694 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "BSPTree.h"
+#include "Polygon.h"
+#include "PolygonTestUtils.h"
+
+#include <deque>
+#include <list>
+
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+typedef mozilla::gfx::Polygon MozPolygon;
+
+namespace {
+
+static void RunTest(std::deque<MozPolygon> aPolygons,
+ std::deque<MozPolygon> aExpected) {
+ std::list<LayerPolygon> layers;
+ for (MozPolygon& polygon : aPolygons) {
+ layers.push_back(LayerPolygon(nullptr, std::move(polygon)));
+ }
+
+ const BSPTree tree(layers);
+ const nsTArray<LayerPolygon> order = tree.GetDrawOrder();
+
+ EXPECT_EQ(aExpected.size(), order.Length());
+
+ for (size_t i = 0; i < order.Length(); ++i) {
+ EXPECT_TRUE(aExpected[i] == *order[i].geometry);
+ }
+}
+
+} // namespace
+
+TEST(BSPTree, SameNode)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)},
+ MozPolygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)}};
+
+ ::RunTest(polygons, polygons);
+}
+
+TEST(BSPTree, OneChild)
+{
+ const MozPolygon p1{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)};
+
+ const MozPolygon p2{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f)};
+
+ ::RunTest({p1, p2}, {p1, p2});
+ ::RunTest({p2, p1}, {p1, p2});
+}
+
+TEST(BSPTree, SharedEdge1)
+{
+ MozPolygon p1{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(2.0f, 2.0f, 1.0f, 1.0f), Point4D(2.0f, 0.0f, 1.0f, 1.0f)};
+
+ ::RunTest({p1, p2}, {p1, p2});
+}
+
+TEST(BSPTree, SharedEdge2)
+{
+ MozPolygon p1{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(2.0f, 2.0f, 0.0f, 1.0f), Point4D(2.0f, 0.0f, 0.0f, 1.0f)};
+
+ ::RunTest({p1, p2}, {p2, p1});
+}
+
+TEST(BSPTree, SplitSharedEdge)
+{
+ MozPolygon p1{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(1.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 1.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f)};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 0.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f)},
+ MozPolygon{
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f)},
+ MozPolygon{
+ Point4D(1.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 1.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f)}};
+
+ ::RunTest({p1, p2}, expected);
+}
+
+TEST(BSPTree, SplitSimple1)
+{
+ MozPolygon p1{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f)};
+
+ MozPolygon p2{
+ Point4D(0.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 0.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f)};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{
+ Point4D(0.0f, 1.0f, 0.0f, 1.0f), Point4D(0.0f, 0.5f, 1.0f, 1.0f),
+ Point4D(1.0f, 0.5f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 0.0f, 1.0f)},
+ p1,
+ MozPolygon{
+ Point4D(0.0f, 0.0f, 2.0f, 1.0f), Point4D(1.0f, 0.0f, 2.0f, 1.0f),
+ Point4D(1.0f, 0.5f, 1.0f, 1.0f), Point4D(0.0f, 0.5f, 1.0f, 1.0f)}};
+
+ ::RunTest({p1, p2}, expected);
+}
+
+TEST(BSPTree, SplitSimple2)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, -5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, 5.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(0.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, 0.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, NoSplit1)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(0.00000f, 10.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, -5.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 5.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(0.00000f, 10.00000f, -5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, -5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, -5.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, 0.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 0.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 10.00000f, 5.00000f, 1.0f),
+ Point4D(0.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 0.00000f, 5.00000f, 1.0f),
+ Point4D(10.00000f, 10.00000f, 5.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, NoSplit2)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)},
+ MozPolygon{Point4D(0.00000f, 5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -10.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -10.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(0.00000f, 5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -15.00000f, 1.0f),
+ Point4D(0.00000f, -5.00000f, -10.00000f, 1.0f),
+ Point4D(0.00000f, 5.00000f, -10.00000f, 1.0f)},
+ MozPolygon{Point4D(-5.00000f, -5.00000f, 0.00000f, 1.0f),
+ Point4D(-5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, 5.00000f, 0.00000f, 1.0f),
+ Point4D(5.00000f, -5.00000f, 0.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate0degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, 2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, 0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00000f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate20degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate40degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate60degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate80degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate100degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate120degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(-0.00000f, -2.73200f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate140degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate160degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(-0.00000f, -2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate180degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate200degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate220degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate240degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(-0.00000f, -0.73200f, -2.73200f, 1.0f),
+ Point4D(0.00010f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(0.00010f, 0.73210f, 2.73210f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.26793f, 0.73210f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(-2.00000f, 1.26793f, 0.73210f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate260degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(-0.00000f, -2.56340f, 1.19540f, 1.0f),
+ Point4D(0.00010f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(0.00010f, 2.56350f, -1.19530f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate280degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, -1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(-0.00000f, 2.73210f, -0.73200f, 1.0f),
+ Point4D(-0.00000f, 0.73210f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, -2.73200f, 1.0f)},
+ MozPolygon{Point4D(-2.00000f, -1.26783f, -0.73200f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, 1.73210f, 1.00010f, 1.0f),
+ Point4D(2.00000f, -1.26783f, -0.73200f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate300degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, 0.73210f, -2.73200f, 1.0f),
+ Point4D(-0.00000f, 2.73210f, 0.73210f, 1.0f),
+ Point4D(0.00010f, -0.73200f, 2.73210f, 1.0f),
+ Point4D(0.00010f, -2.73200f, -0.73200f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 1.73210f, -0.99990f, 1.0f),
+ Point4D(2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, -1.73200f, 1.00000f, 1.0f),
+ Point4D(-2.00000f, 1.73210f, -0.99990f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate320degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -1.19530f, -2.56340f, 1.0f),
+ Point4D(-0.00000f, 2.56350f, -1.19530f, 1.0f),
+ Point4D(0.00010f, 1.19540f, 2.56350f, 1.0f),
+ Point4D(0.00010f, -2.56340f, 1.19540f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.68410f, -1.87930f, 1.0f),
+ Point4D(2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, -0.68400f, 1.87940f, 1.0f),
+ Point4D(-2.00000f, 0.68410f, -1.87930f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate340degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
+
+TEST(BSPTree, TwoPlaneIntersectRotate360degrees)
+{
+ const std::deque<MozPolygon> polygons{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+
+ const std::deque<MozPolygon> expected{
+ MozPolygon{Point4D(-0.00000f, -2.00000f, -2.00000f, 1.0f),
+ Point4D(-0.00000f, 2.00000f, -2.00000f, 1.0f),
+ Point4D(0.00010f, 2.00000f, 2.00000f, 1.0f),
+ Point4D(0.00010f, -2.00000f, 2.00000f, 1.0f)},
+ MozPolygon{Point4D(2.00000f, 0.00010f, -2.00000f, 1.0f),
+ Point4D(2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, -0.00000f, 2.00000f, 1.0f),
+ Point4D(-2.00000f, 0.00010f, -2.00000f, 1.0f)}};
+ ::RunTest(polygons, expected);
+}
diff --git a/gfx/tests/gtest/TestBufferRotation.cpp b/gfx/tests/gtest/TestBufferRotation.cpp
new file mode 100644
index 0000000000..871295e68c
--- /dev/null
+++ b/gfx/tests/gtest/TestBufferRotation.cpp
@@ -0,0 +1,162 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "BufferUnrotate.h"
+
+using mozilla::gfx::BufferUnrotate;
+
+static unsigned char* GenerateBuffer(int bytesPerPixel, int width, int height,
+ int stride, int xBoundary, int yBoundary) {
+ unsigned char* buffer = new unsigned char[stride * height];
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pos = ((yBoundary + y) % height) * stride +
+ ((xBoundary + x) % width) * bytesPerPixel;
+ for (int i = 0; i < bytesPerPixel; i++) {
+ buffer[pos + i] = (x + y + i * 2) % 256;
+ }
+ }
+ }
+ return buffer;
+}
+
+static bool CheckBuffer(unsigned char* buffer, int bytesPerPixel, int width,
+ int height, int stride) {
+ int xBoundary = 0;
+ int yBoundary = 0;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ int pos = ((yBoundary + y) % height) * stride +
+ ((xBoundary + x) % width) * bytesPerPixel;
+ for (int i = 0; i < bytesPerPixel; i++) {
+ if (buffer[pos + i] != (x + y + i * 2) % 256) {
+ printf("Buffer differs at %i, %i, is %i\n", x, y,
+ (int)buffer[pos + i]);
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+TEST(Gfx, BufferUnrotateHorizontal)
+{
+ const int NUM_OF_TESTS = 8;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
+ int yBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateVertical)
+{
+ const int NUM_OF_TESTS = 8;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {0, 0, 0, 0};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateBoth)
+{
+ const int NUM_OF_TESTS = 16;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {100, 100, 99, 99, 100, 100, 99, 99,
+ 100, 100, 99, 99, 100, 100, 99, 99};
+ int height[NUM_OF_TESTS] = {100, 99, 100, 99, 100, 99, 100, 99,
+ 100, 99, 100, 99, 100, 99, 100, 99};
+ int xBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 31, 31, 31, 31,
+ 30, 30, 30, 30, 31, 31, 31, 31};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 30, 30, 30, 30, 30, 30,
+ 31, 31, 31, 31, 31, 31, 31, 31};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
+
+TEST(Gfx, BufferUnrotateUneven)
+{
+ const int NUM_OF_TESTS = 16;
+ int bytesPerPixelList[2] = {2, 4};
+ int width[NUM_OF_TESTS] = {10, 100, 99, 39, 100, 40, 99, 39,
+ 100, 50, 39, 99, 74, 60, 99, 39};
+ int height[NUM_OF_TESTS] = {100, 39, 10, 99, 10, 99, 40, 99,
+ 73, 39, 100, 39, 67, 99, 84, 99};
+ int xBoundary[NUM_OF_TESTS] = {0, 0, 30, 30, 99, 31, 0, 31,
+ 30, 30, 30, 30, 31, 31, 31, 38};
+ int yBoundary[NUM_OF_TESTS] = {30, 30, 0, 30, 0, 30, 0, 30,
+ 31, 31, 31, 31, 31, 31, 31, 98};
+
+ for (int bytesPerId = 0; bytesPerId < 2; bytesPerId++) {
+ int bytesPerPixel = bytesPerPixelList[bytesPerId];
+ int stride = 256 * bytesPerPixel;
+ for (int testId = 0; testId < NUM_OF_TESTS; testId++) {
+ unsigned char* buffer =
+ GenerateBuffer(bytesPerPixel, width[testId], height[testId], stride,
+ xBoundary[testId], yBoundary[testId]);
+ BufferUnrotate(buffer, width[testId] * bytesPerPixel, height[testId],
+ stride, xBoundary[testId] * bytesPerPixel,
+ yBoundary[testId]);
+
+ EXPECT_TRUE(CheckBuffer(buffer, bytesPerPixel, width[testId],
+ height[testId], stride));
+ delete[] buffer;
+ }
+ }
+}
diff --git a/gfx/tests/gtest/TestColorNames.cpp b/gfx/tests/gtest/TestColorNames.cpp
new file mode 100644
index 0000000000..dba82161a7
--- /dev/null
+++ b/gfx/tests/gtest/TestColorNames.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include <string.h>
+#include "nsColor.h"
+#include "nsColorNames.h"
+#include "mozilla/Sprintf.h"
+#include "nsString.h"
+#include "mozilla/ArrayUtils.h"
+
+// define an array of all color names
+#define GFX_COLOR(_name, _value) #_name,
+static const char* const kColorNames[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+// define an array of all color name values
+#define GFX_COLOR(_name, _value) _value,
+static const nscolor kColors[] = {
+#include "nsColorNameList.h"
+};
+#undef GFX_COLOR
+
+using namespace mozilla;
+
+static const char* kJunkNames[] = {nullptr, "", "123",
+ "backgroundz", "zzzzzz", "#@$&@#*@*$@$#"};
+
+static void RunColorTests() {
+ nscolor rgb;
+ // First make sure we can find all of the tags that are supposed to
+ // be in the table. Futz with the case to make sure any case will
+ // work
+
+ for (uint32_t index = 0; index < ArrayLength(kColorNames); index++) {
+ // Lookup color by name and make sure it has the right id
+ nsCString tagName(kColorNames[index]);
+
+ // Check that color lookup by name gets the right rgb value
+ ASSERT_TRUE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tagName), &rgb))
+ << "can't find '" << tagName.get() << "'";
+ ASSERT_TRUE((rgb == kColors[index]))
+ << "failed at index " << index << " out of " << ArrayLength(kColorNames);
+
+ // fiddle with the case to make sure we can still find it
+ tagName.SetCharAt(tagName.CharAt(0) - 32, 0);
+ ASSERT_TRUE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tagName), &rgb))
+ << "can't find '" << tagName.get() << "'";
+ ASSERT_TRUE((rgb == kColors[index]))
+ << "failed at index " << index << " out of " << ArrayLength(kColorNames);
+
+ // Check that parsing an RGB value in hex gets the right values
+ uint8_t r = NS_GET_R(rgb);
+ uint8_t g = NS_GET_G(rgb);
+ uint8_t b = NS_GET_B(rgb);
+ uint8_t a = NS_GET_A(rgb);
+ char cbuf[50];
+ if (a != UINT8_MAX) {
+ SprintfLiteral(cbuf, "%02x%02x%02x%02x", r, g, b, a);
+ } else {
+ SprintfLiteral(cbuf, "%02x%02x%02x", r, g, b);
+ }
+ nscolor hexrgb;
+ ASSERT_TRUE(NS_HexToRGBA(NS_ConvertASCIItoUTF16(cbuf),
+ nsHexColorType::AllowAlpha, &hexrgb))
+ << "hex conversion to color of '" << cbuf << "'";
+ ASSERT_TRUE(hexrgb == rgb);
+ }
+}
+
+static void RunJunkColorTests() {
+ nscolor rgb;
+ // Now make sure we don't find some garbage
+ for (uint32_t i = 0; i < ArrayLength(kJunkNames); i++) {
+ nsCString tag(kJunkNames[i]);
+ ASSERT_FALSE(NS_ColorNameToRGB(NS_ConvertASCIItoUTF16(tag), &rgb))
+ << "Failed at junk color " << kJunkNames[i];
+ }
+}
+
+TEST(Gfx, ColorNames)
+{ RunColorTests(); }
+
+TEST(Gfx, JunkColorNames)
+{ RunJunkColorTests(); }
diff --git a/gfx/tests/gtest/TestCompositor.cpp b/gfx/tests/gtest/TestCompositor.cpp
new file mode 100644
index 0000000000..fe8cce8d90
--- /dev/null
+++ b/gfx/tests/gtest/TestCompositor.cpp
@@ -0,0 +1,212 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gfxUtils.h"
+#include "gtest/gtest.h"
+#include "TestLayers.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/layers/BasicCompositor.h" // for BasicCompositor
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/CompositorOGL.h" // for CompositorOGL
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "MockWidget.h"
+#include "nsBaseWidget.h"
+#include <vector>
+
+const int gCompWidth = 256;
+const int gCompHeight = 256;
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using namespace mozilla::gl;
+
+struct LayerManagerData {
+ RefPtr<MockWidget> mWidget;
+ RefPtr<Compositor> mCompositor;
+ RefPtr<widget::CompositorWidget> mCompositorWidget;
+ RefPtr<LayerManagerComposite> mLayerManager;
+
+ LayerManagerData(Compositor* compositor, MockWidget* widget,
+ widget::CompositorWidget* aWidget,
+ LayerManagerComposite* layerManager)
+ : mWidget(widget),
+ mCompositor(compositor),
+ mCompositorWidget(aWidget),
+ mLayerManager(layerManager) {}
+};
+
+static already_AddRefed<Compositor> CreateTestCompositor(
+ LayersBackend backend, widget::CompositorWidget* widget) {
+ gfxPlatform::GetPlatform();
+
+ RefPtr<Compositor> compositor;
+
+ if (backend == LayersBackend::LAYERS_OPENGL) {
+ compositor =
+ new CompositorOGL(nullptr, widget, gCompWidth, gCompHeight, true);
+ compositor->SetDestinationSurfaceSize(IntSize(gCompWidth, gCompHeight));
+ } else if (backend == LayersBackend::LAYERS_BASIC) {
+ compositor = new BasicCompositor(nullptr, widget);
+#ifdef XP_WIN
+ } else if (backend == LayersBackend::LAYERS_D3D11) {
+ // compositor = new CompositorD3D11();
+ MOZ_CRASH(); // No support yet
+#endif
+ }
+ nsCString failureReason;
+ if (!compositor || !compositor->Initialize(&failureReason)) {
+ printf_stderr(
+ "Failed to construct layer manager for the requested backend\n");
+ abort();
+ }
+
+ return compositor.forget();
+}
+
+/**
+ * Get a list of layers managers for the platform to run the test on.
+ */
+static std::vector<LayerManagerData> GetLayerManagers(
+ std::vector<LayersBackend> aBackends) {
+ std::vector<LayerManagerData> managers;
+
+ for (size_t i = 0; i < aBackends.size(); i++) {
+ auto backend = aBackends[i];
+
+ RefPtr<MockWidget> widget = new MockWidget(gCompWidth, gCompHeight);
+ CompositorOptions options;
+ RefPtr<widget::CompositorWidget> proxy =
+ new widget::InProcessCompositorWidget(options, widget);
+ RefPtr<Compositor> compositor = CreateTestCompositor(backend, proxy);
+
+ RefPtr<LayerManagerComposite> layerManager =
+ new LayerManagerComposite(compositor);
+
+ managers.push_back(
+ LayerManagerData(compositor, widget, proxy, layerManager));
+ }
+
+ return managers;
+}
+
+/**
+ * This will return the default list of backends that
+ * units test should run against.
+ */
+static std::vector<LayersBackend> GetPlatformBackends() {
+ std::vector<LayersBackend> backends;
+
+ // For now we only support Basic for gtest
+ backends.push_back(LayersBackend::LAYERS_BASIC);
+
+#ifdef XP_MACOSX
+ backends.push_back(LayersBackend::LAYERS_OPENGL);
+#endif
+
+ // TODO Support OGL/D3D backends with unit test
+ return backends;
+}
+
+static already_AddRefed<DrawTarget> CreateDT() {
+ return gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(
+ IntSize(gCompWidth, gCompHeight), SurfaceFormat::B8G8R8A8);
+}
+
+static bool CompositeAndCompare(RefPtr<LayerManagerComposite> layerManager,
+ DrawTarget* refDT) {
+ RefPtr<DrawTarget> drawTarget = CreateDT();
+
+ layerManager->BeginTransactionWithDrawTarget(
+ drawTarget, IntRect(0, 0, gCompWidth, gCompHeight));
+ layerManager->EndTransaction(TimeStamp::Now());
+
+ RefPtr<SourceSurface> ss = drawTarget->Snapshot();
+ RefPtr<DataSourceSurface> dss = ss->GetDataSurface();
+ DataSourceSurface::ScopedMap dssMap(dss, DataSourceSurface::READ);
+ uint8_t* bitmap = dssMap.GetData();
+
+ RefPtr<SourceSurface> ssRef = refDT->Snapshot();
+ RefPtr<DataSourceSurface> dssRef = ssRef->GetDataSurface();
+ DataSourceSurface::ScopedMap dssRefMap(dssRef, DataSourceSurface::READ);
+ uint8_t* bitmapRef = dssRefMap.GetData();
+
+ for (int y = 0; y < gCompHeight; y++) {
+ for (int x = 0; x < gCompWidth; x++) {
+ for (size_t channel = 0; channel < 4; channel++) {
+ uint8_t bit = bitmap[y * dssMap.GetStride() + x * 4 + channel];
+ uint8_t bitRef = bitmapRef[y * dssRefMap.GetStride() + x * 4 + channel];
+ if (bit != bitRef) {
+ printf("Layer Tree:\n");
+ layerManager->Dump();
+ printf("Original:\n");
+ gfxUtils::DumpAsDataURI(drawTarget);
+ printf("\n\n");
+
+ printf("Reference:\n");
+ gfxUtils::DumpAsDataURI(refDT);
+ printf("\n\n");
+
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+TEST(Gfx, CompositorConstruct)
+{ auto layerManagers = GetLayerManagers(GetPlatformBackends()); }
+
+TEST(Gfx, CompositorSimpleTree)
+{
+ auto layerManagers = GetLayerManagers(GetPlatformBackends());
+ for (size_t i = 0; i < layerManagers.size(); i++) {
+ RefPtr<LayerManagerComposite> layerManager = layerManagers[i].mLayerManager;
+ RefPtr<LayerManager> lmBase = layerManager.get();
+ nsTArray<RefPtr<Layer>> layers;
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+ nsIntRegion(IntRect(0, 0, gCompWidth, gCompHeight)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 50, 100, 100)),
+ };
+ RefPtr<Layer> root =
+ CreateLayerTree("c(ooo)", layerVisibleRegion, nullptr, lmBase, layers);
+
+ { // background
+ ColorLayer* colorLayer = layers[1]->AsColorLayer();
+ colorLayer->SetColor(DeviceColor(1.f, 0.f, 1.f, 1.f));
+ colorLayer->SetBounds(
+ colorLayer->GetVisibleRegion().GetBounds().ToUnknownRect());
+ }
+
+ {
+ ColorLayer* colorLayer = layers[2]->AsColorLayer();
+ colorLayer->SetColor(DeviceColor(1.f, 0.f, 0.f, 1.f));
+ colorLayer->SetBounds(
+ colorLayer->GetVisibleRegion().GetBounds().ToUnknownRect());
+ }
+
+ {
+ ColorLayer* colorLayer = layers[3]->AsColorLayer();
+ colorLayer->SetColor(DeviceColor(0.f, 0.f, 1.f, 1.f));
+ colorLayer->SetBounds(
+ colorLayer->GetVisibleRegion().GetBounds().ToUnknownRect());
+ }
+
+ RefPtr<DrawTarget> refDT = CreateDT();
+ refDT->FillRect(Rect(0, 0, gCompWidth, gCompHeight),
+ ColorPattern(DeviceColor(1.f, 0.f, 1.f, 1.f)));
+ refDT->FillRect(Rect(0, 0, 100, 100),
+ ColorPattern(DeviceColor(1.f, 0.f, 0.f, 1.f)));
+ refDT->FillRect(Rect(0, 50, 100, 100),
+ ColorPattern(DeviceColor(0.f, 0.f, 1.f, 1.f)));
+ EXPECT_TRUE(CompositeAndCompare(layerManager, refDT));
+ }
+}
diff --git a/gfx/tests/gtest/TestConfigManager.cpp b/gfx/tests/gtest/TestConfigManager.cpp
new file mode 100644
index 0000000000..6843723e04
--- /dev/null
+++ b/gfx/tests/gtest/TestConfigManager.cpp
@@ -0,0 +1,921 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "gtest/gtest.h"
+
+#include "gfxFeature.h"
+#include "mozilla/gfx/gfxConfigManager.h"
+#include "nsIGfxInfo.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+namespace mozilla {
+namespace gfx {
+
+class MockGfxInfo final : public nsIGfxInfo {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ int32_t mStatusWr;
+ int32_t mStatusWrCompositor;
+ int32_t mStatusWrSoftware;
+ int32_t mMaxRefreshRate;
+ bool mHasMixedRefreshRate;
+ Maybe<bool> mHasBattery;
+ const char* mVendorId;
+
+ // Default allows WebRender + compositor, and is desktop NVIDIA.
+ MockGfxInfo()
+ : mStatusWr(nsIGfxInfo::FEATURE_ALLOW_ALWAYS),
+ mStatusWrCompositor(nsIGfxInfo::FEATURE_STATUS_OK),
+ mStatusWrSoftware(nsIGfxInfo::FEATURE_DENIED),
+ mMaxRefreshRate(-1),
+ mHasMixedRefreshRate(false),
+ mHasBattery(Some(false)),
+ mVendorId("0x10de") {}
+
+ NS_IMETHOD GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
+ int32_t* _retval) override {
+ switch (aFeature) {
+ case nsIGfxInfo::FEATURE_WEBRENDER:
+ *_retval = mStatusWr;
+ break;
+ case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
+ *_retval = mStatusWrCompositor;
+ break;
+ case nsIGfxInfo::FEATURE_WEBRENDER_SOFTWARE:
+ *_retval = mStatusWrSoftware;
+ break;
+ default:
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetHasBattery(bool* aHasBattery) override {
+ if (mHasBattery.isNothing()) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ *aHasBattery = *mHasBattery;
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetAdapterVendorID(nsAString& aAdapterVendorID) override {
+ if (!mVendorId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ aAdapterVendorID.AssignASCII(mVendorId);
+ return NS_OK;
+ }
+
+ NS_IMETHOD_(int32_t) GetMaxRefreshRate(bool* aMixed) override {
+ if (aMixed) {
+ *aMixed = mHasMixedRefreshRate;
+ }
+ return mMaxRefreshRate;
+ }
+
+ NS_IMETHODIMP GetEmbeddedInFirefoxReality(
+ bool* aEmbeddedInFirefoxReality) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ // The following methods we don't need for testing gfxConfigManager.
+ NS_IMETHOD GetFeatureSuggestedDriverVersion(int32_t aFeature,
+ nsAString& _retval) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+
+ NS_IMETHOD GetMonitors(JSContext* cx,
+ JS::MutableHandleValue _retval) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetFailures(nsTArray<int32_t>& indices,
+ nsTArray<nsCString>& failures) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD_(void) LogFailure(const nsACString& failure) override {}
+ NS_IMETHOD GetInfo(JSContext*, JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetFeatures(JSContext*, JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetFeatureLog(JSContext*, JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetActiveCrashGuards(JSContext*,
+ JS::MutableHandle<JS::Value>) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetContentBackend(nsAString& aContentBackend) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetUsingGPUProcess(bool* aOutValue) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetWebRenderEnabled(bool* aWebRenderEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetIsHeadless(bool* aIsHeadless) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetUsesTiling(bool* aUsesTiling) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetContentUsesTiling(bool* aUsesTiling) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetOffMainThreadPaintEnabled(
+ bool* aOffMainThreadPaintEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetOffMainThreadPaintWorkerCount(
+ int32_t* aOffMainThreadPaintWorkerCount) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetTargetFrameRate(uint32_t* aTargetFrameRate) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetD2DEnabled(bool* aD2DEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDWriteEnabled(bool* aDWriteEnabled) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDWriteVersion(nsAString& aDwriteVersion) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetCleartypeParameters(nsAString& aCleartypeParams) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetWindowProtocol(nsAString& aWindowProtocol) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDesktopEnvironment(nsAString& aDesktopEnvironment) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDescription(nsAString& aAdapterDescription) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriver(nsAString& aAdapterDriver) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDeviceID(nsAString& aAdapterDeviceID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterSubsysID(nsAString& aAdapterSubsysID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterRAM(uint32_t* aAdapterRAM) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVendor(nsAString& aAdapterDriverVendor) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVersion(
+ nsAString& aAdapterDriverVersion) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverDate(nsAString& aAdapterDriverDate) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDescription2(nsAString& aAdapterDescription) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriver2(nsAString& aAdapterDriver) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterVendorID2(nsAString& aAdapterVendorID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDeviceID2(nsAString& aAdapterDeviceID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterSubsysID2(nsAString& aAdapterSubsysID) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterRAM2(uint32_t* aAdapterRAM) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVendor2(nsAString& aAdapterDriverVendor) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverVersion2(
+ nsAString& aAdapterDriverVersion) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetAdapterDriverDate2(nsAString& aAdapterDriverDate) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetIsGPU2Active(bool* aIsGPU2Active) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDisplayInfo(nsTArray<nsString>& aDisplayInfo) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDisplayWidth(nsTArray<uint32_t>& aDisplayWidth) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDisplayHeight(nsTArray<uint32_t>& aDisplayHeight) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD GetDrmRenderDevice(nsACString& aDrmRenderDevice) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD ControlGPUProcessForXPCShell(bool aEnable,
+ bool* _retval) override {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ NS_IMETHOD_(void) GetData() override {}
+
+ private:
+ virtual ~MockGfxInfo() = default;
+};
+
+NS_IMPL_ISUPPORTS(MockGfxInfo, nsIGfxInfo)
+
+class GfxConfigManager : public ::testing::Test, public gfxConfigManager {
+ public:
+ GfxConfigManager() {
+ mFeatureWr = &mFeatures.mWr;
+ mFeatureWrQualified = &mFeatures.mWrQualified;
+ mFeatureWrCompositor = &mFeatures.mWrCompositor;
+ mFeatureWrAngle = &mFeatures.mWrAngle;
+ mFeatureWrDComp = &mFeatures.mWrDComp;
+ mFeatureWrPartial = &mFeatures.mWrPartial;
+ mFeatureHwCompositing = &mFeatures.mHwCompositing;
+ mFeatureD3D11HwAngle = &mFeatures.mD3D11HwAngle;
+ mFeatureGPUProcess = &mFeatures.mGPUProcess;
+ mFeatureWrSoftware = &mFeatures.mWrSoftware;
+ }
+
+ void SetUp() override {
+ mMockGfxInfo = new MockGfxInfo();
+ mGfxInfo = mMockGfxInfo;
+
+ // By default, turn everything on. This effectively assumes we are on
+ // qualified nightly Windows 10 with DirectComposition support.
+ mFeatureHwCompositing->EnableByDefault();
+ mFeatureD3D11HwAngle->EnableByDefault();
+ mFeatureGPUProcess->EnableByDefault();
+
+ mWrCompositorEnabled.emplace(true);
+ mWrPartialPresent = true;
+ mWrForceAngle = true;
+ mWrDCompWinEnabled = true;
+ mWrCompositorDCompRequired = true;
+ ++mHwStretchingSupport.mBoth;
+ mIsWin10OrLater = true;
+ mIsNightly = true;
+ }
+
+ void TearDown() override {
+ mFeatures.mWr.Reset();
+ mFeatures.mWrQualified.Reset();
+ mFeatures.mWrCompositor.Reset();
+ mFeatures.mWrAngle.Reset();
+ mFeatures.mWrDComp.Reset();
+ mFeatures.mWrPartial.Reset();
+ mFeatures.mHwCompositing.Reset();
+ mFeatures.mD3D11HwAngle.Reset();
+ mFeatures.mGPUProcess.Reset();
+ mFeatures.mWrSoftware.Reset();
+ }
+
+ struct {
+ FeatureState mWr;
+ FeatureState mWrQualified;
+ FeatureState mWrCompositor;
+ FeatureState mWrAngle;
+ FeatureState mWrDComp;
+ FeatureState mWrPartial;
+ FeatureState mHwCompositing;
+ FeatureState mD3D11HwAngle;
+ FeatureState mGPUProcess;
+ FeatureState mWrSoftware;
+ } mFeatures;
+
+ RefPtr<MockGfxInfo> mMockGfxInfo;
+};
+
+} // namespace gfx
+} // namespace mozilla
+
+TEST_F(GfxConfigManager, WebRenderDefault) {
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNoPartialPresent) {
+ mWrPartialPresent = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderScaledResolutionWithHwStretching) {
+ mScaledResolution = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderScaledResolutionNoHwStretching) {
+ ++mHwStretchingSupport.mNone;
+ mScaledResolution = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderEnabledWithDisableHwCompositingNoWr) {
+ mDisableHwCompositingNoWr = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDisabledWithDisableHwCompositingNoWr) {
+ mDisableHwCompositingNoWr = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_FALSE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_FALSE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDisabledWithAllowSoftwareGPUProcess) {
+ mDisableHwCompositingNoWr = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mGPUProcessAllowSoftware = true;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_FALSE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSafeMode) {
+ mSafeMode = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderEarlierThanWindows10) {
+ mIsWin10OrLater = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDCompDisabled) {
+ mWrDCompWinEnabled = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderDCompNotRequired) {
+ mWrDCompWinEnabled = false;
+ mWrCompositorDCompRequired = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceAngleDisabled) {
+ mWrForceAngle = false;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderD3D11HwAngleDisabled) {
+ mFeatures.mD3D11HwAngle.UserDisable("", ""_ns);
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_FALSE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderD3D11HwAngleAndForceAngleDisabled) {
+ mWrForceAngle = false;
+ mFeatures.mD3D11HwAngle.UserDisable("", ""_ns);
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_FALSE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderGPUProcessDisabled) {
+ mFeatures.mGPUProcess.UserDisable("", ""_ns);
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_FALSE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderQualifiedAndBlocklistAllowQualified) {
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderQualifiedAndBlocklistAllowAlways) {
+ mIsNightly = false;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelBatteryNoHwStretchingNotNightly) {
+ mIsNightly = false;
+ ++mHwStretchingSupport.mNone;
+ mScaledResolution = true;
+ mMockGfxInfo->mHasBattery.ref() = true;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelHighRefreshRateNightly) {
+ mIsNightly = true;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelHighRefreshRateNotNightly) {
+ mIsNightly = false;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderIntelAtRefreshRateThreshold) {
+ mIsNightly = false;
+ mMockGfxInfo->mMaxRefreshRate = 75;
+ mMockGfxInfo->mVendorId = "0x8086";
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaHighMixedRefreshRateNightly) {
+ mIsNightly = true;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mHasMixedRefreshRate = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaHighMixedRefreshRateNotNightly) {
+ mIsNightly = false;
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ mMockGfxInfo->mHasMixedRefreshRate = true;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaHighRefreshRateNotNightly) {
+ mMockGfxInfo->mMaxRefreshRate = 120;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderNvidiaLowMixedRefreshRateNotNightly) {
+ mMockGfxInfo->mMaxRefreshRate = 60;
+ mMockGfxInfo->mHasMixedRefreshRate = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderWhenXRenderEnabled) {
+ mXRenderEnabled = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSofwareAndQualified) {
+ // Enabling software in gfxInfo gives the same results
+ // as the default configuration, since qualified hardware
+ // takes precedence, but we still enable the software feature.
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSofwareAndNotQualified) {
+ // Enabling software in gfxInfo when we're not qualified
+ // results in WR software being enabled instead.
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceDisabledEnvvar) {
+ mWrEnvForceDisabled = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceDisabledEnvvar) {
+ mWrEnvForceDisabled = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceDisabledPref) {
+ mWrForceDisabled = true;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceSoftwareForceDisabledEnvvar) {
+ mWrEnvForceDisabled = true;
+ mWrSoftwareForceEnabled = true;
+ ConfigureWebRender();
+
+ EXPECT_TRUE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceEnabledEnvvar) {
+ mWrEnvForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceEnabledEnvvar) {
+ mWrEnvForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderSoftwareAllowedForceEnabledPref) {
+ mWrForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ mMockGfxInfo->mStatusWrSoftware = nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWr.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrSoftware.IsEnabled());
+}
+
+TEST_F(GfxConfigManager, WebRenderForceSoftwareForceEnabledEnvvar) {
+ mWrEnvForceEnabled = true;
+ mWrSoftwareForceEnabled = true;
+ mMockGfxInfo->mStatusWr = nsIGfxInfo::FEATURE_DENIED;
+ ConfigureWebRender();
+
+ EXPECT_FALSE(mFeatures.mWrQualified.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWr.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrCompositor.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrAngle.IsEnabled());
+ EXPECT_FALSE(mFeatures.mWrDComp.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrPartial.IsEnabled());
+ EXPECT_TRUE(mFeatures.mHwCompositing.IsEnabled());
+ EXPECT_TRUE(mFeatures.mGPUProcess.IsEnabled());
+ EXPECT_TRUE(mFeatures.mD3D11HwAngle.IsEnabled());
+ EXPECT_TRUE(mFeatures.mWrSoftware.IsEnabled());
+}
diff --git a/gfx/tests/gtest/TestGfxWidgets.cpp b/gfx/tests/gtest/TestGfxWidgets.cpp
new file mode 100644
index 0000000000..90bfce863a
--- /dev/null
+++ b/gfx/tests/gtest/TestGfxWidgets.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "GfxDriverInfo.h"
+#include "nsVersionComparator.h"
+
+using namespace mozilla::widget;
+
+TEST(GfxWidgets, Split)
+{
+ char aStr[8], bStr[8], cStr[8], dStr[8];
+
+ ASSERT_TRUE(SplitDriverVersion("33.4.3.22", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 33 && atoi(bStr) == 4 && atoi(cStr) == 3 &&
+ atoi(dStr) == 22);
+
+ ASSERT_TRUE(SplitDriverVersion("28.74.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 74 && atoi(cStr) == 0 &&
+ atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("132.0.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 132 && atoi(bStr) == 0 && atoi(cStr) == 0 &&
+ atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("2.3.0.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 2 && atoi(bStr) == 3 && atoi(cStr) == 0 &&
+ atoi(dStr) == 0);
+
+ ASSERT_TRUE(SplitDriverVersion("25.4.0.8", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_TRUE(SplitDriverVersion("424.143.84437.3", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8443 &&
+ atoi(dStr) == 3);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8.", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_TRUE(SplitDriverVersion("424.143.8.3143243", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8 &&
+ atoi(dStr) == 3143);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8..", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_FALSE(
+ SplitDriverVersion("424.143.8.3143243.", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 424 && atoi(bStr) == 143 && atoi(cStr) == 8 &&
+ atoi(dStr) == 3143);
+
+ ASSERT_FALSE(SplitDriverVersion("25.4.0.8.13", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 25 && atoi(bStr) == 4 && atoi(cStr) == 0 &&
+ atoi(dStr) == 8);
+
+ ASSERT_FALSE(SplitDriverVersion("4.1.8.13.24.35", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 4 && atoi(bStr) == 1 && atoi(cStr) == 8 &&
+ atoi(dStr) == 13);
+
+ ASSERT_TRUE(SplitDriverVersion("28...74", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 28 && atoi(bStr) == 0 && atoi(cStr) == 0 &&
+ atoi(dStr) == 74);
+
+ ASSERT_FALSE(SplitDriverVersion("4.1.8.13.24.35", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 4 && atoi(bStr) == 1 && atoi(cStr) == 8 &&
+ atoi(dStr) == 13);
+
+ ASSERT_TRUE(SplitDriverVersion("35..42.0", aStr, bStr, cStr, dStr));
+ ASSERT_TRUE(atoi(aStr) == 35 && atoi(bStr) == 0 && atoi(cStr) == 42 &&
+ atoi(dStr) == 0);
+}
+
+TEST(GfxWidgets, Versioning)
+{
+ ASSERT_TRUE(mozilla::Version("0") < mozilla::Version("41.0a1"));
+ ASSERT_TRUE(mozilla::Version("39.0.5b7") < mozilla::Version("41.0a1"));
+ ASSERT_TRUE(mozilla::Version("18.0.5b7") < mozilla::Version("18.2"));
+ ASSERT_TRUE(mozilla::Version("30.0.5b7") < mozilla::Version("41.0b9"));
+ ASSERT_TRUE(mozilla::Version("100") > mozilla::Version("43.0a1"));
+ ASSERT_FALSE(mozilla::Version("42.0") < mozilla::Version("42.0"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42.0"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("42"));
+ ASSERT_TRUE(mozilla::Version("42.0b2") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0.5") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.1") < mozilla::Version("43.0a1"));
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42"));
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0.5"));
+ ASSERT_TRUE(mozilla::Version("42.0b7") < mozilla::Version("42.0.5"));
+ ASSERT_TRUE(mozilla::Version("") == mozilla::Version("0"));
+ ASSERT_TRUE(mozilla::Version("1b1b") == mozilla::Version("1b1b"));
+ ASSERT_TRUE(mozilla::Version("1b1b") < mozilla::Version("1b1c"));
+ ASSERT_TRUE(mozilla::Version("1b1b") < mozilla::Version("1b1d"));
+ ASSERT_TRUE(mozilla::Version("1b1c") > mozilla::Version("1b1b"));
+ ASSERT_TRUE(mozilla::Version("1b1d") > mozilla::Version("1b1b"));
+
+ // Note these two; one would expect for 42.0b1 and 42b1 to compare the
+ // same, but they do not. If this ever changes, we want to know, so
+ // leave the test here to fail.
+ ASSERT_TRUE(mozilla::Version("42.0a1") < mozilla::Version("42.0b2"));
+ ASSERT_FALSE(mozilla::Version("42.0a1") < mozilla::Version("42b2"));
+}
diff --git a/gfx/tests/gtest/TestLayers.cpp b/gfx/tests/gtest/TestLayers.cpp
new file mode 100644
index 0000000000..cebd5a9622
--- /dev/null
+++ b/gfx/tests/gtest/TestLayers.cpp
@@ -0,0 +1,505 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "TestLayers.h"
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "LayerUserData.h"
+#include "mozilla/layers/LayerMetricsWrapper.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+class TestContainerLayer : public ContainerLayer {
+ public:
+ explicit TestContainerLayer(LayerManager* aManager)
+ : ContainerLayer(aManager, nullptr) {}
+
+ virtual const char* Name() const { return "TestContainerLayer"; }
+
+ virtual LayerType GetType() const { return TYPE_CONTAINER; }
+
+ virtual void ComputeEffectiveTransforms(
+ const Matrix4x4& aTransformToSurface) {
+ DefaultComputeEffectiveTransforms(aTransformToSurface);
+ }
+};
+
+class TestPaintedLayer : public PaintedLayer {
+ public:
+ explicit TestPaintedLayer(LayerManager* aManager)
+ : PaintedLayer(aManager, nullptr) {}
+
+ virtual const char* Name() const { return "TestPaintedLayer"; }
+
+ virtual LayerType GetType() const { return TYPE_PAINTED; }
+
+ virtual void InvalidateRegion(const nsIntRegion& aRegion) { MOZ_CRASH(); }
+};
+
+class TestLayerManager : public LayerManager {
+ public:
+ TestLayerManager() : LayerManager() {}
+
+ virtual bool EndEmptyTransaction(EndTransactionFlags aFlags = END_DEFAULT) {
+ return false;
+ }
+ virtual already_AddRefed<ContainerLayer> CreateContainerLayer() {
+ RefPtr<ContainerLayer> layer = new TestContainerLayer(this);
+ return layer.forget();
+ }
+ virtual void GetBackendName(nsAString& aName) {}
+ virtual LayersBackend GetBackendType() { return LayersBackend::LAYERS_BASIC; }
+ virtual bool BeginTransaction(const nsCString& = nsCString()) { return true; }
+ virtual already_AddRefed<ImageLayer> CreateImageLayer() {
+ MOZ_CRASH("Not implemented.");
+ }
+ virtual already_AddRefed<PaintedLayer> CreatePaintedLayer() {
+ RefPtr<PaintedLayer> layer = new TestPaintedLayer(this);
+ return layer.forget();
+ }
+ virtual already_AddRefed<ColorLayer> CreateColorLayer() {
+ MOZ_CRASH("Not implemented.");
+ }
+ virtual void SetRoot(Layer* aLayer) {}
+ virtual bool BeginTransactionWithTarget(gfxContext* aTarget,
+ const nsCString& = nsCString()) {
+ return true;
+ }
+ virtual already_AddRefed<CanvasLayer> CreateCanvasLayer() {
+ MOZ_CRASH("Not implemented.");
+ }
+ virtual void EndTransaction(DrawPaintedLayerCallback aCallback,
+ void* aCallbackData,
+ EndTransactionFlags aFlags = END_DEFAULT) {}
+ virtual int32_t GetMaxTextureSize() const { return 0; }
+};
+
+class TestUserData : public LayerUserData {
+ public:
+ MOCK_METHOD0(Die, void());
+ virtual ~TestUserData() { Die(); }
+};
+
+TEST(Layers, LayerConstructor)
+{ TestContainerLayer layer(nullptr); }
+
+TEST(Layers, Defaults)
+{
+ TestContainerLayer layer(nullptr);
+ ASSERT_EQ(1.0, layer.GetOpacity());
+ ASSERT_EQ(1.0f, layer.GetPostXScale());
+ ASSERT_EQ(1.0f, layer.GetPostYScale());
+
+ ASSERT_EQ(nullptr, layer.GetNextSibling());
+ ASSERT_EQ(nullptr, layer.GetPrevSibling());
+ ASSERT_EQ(nullptr, layer.GetFirstChild());
+ ASSERT_EQ(nullptr, layer.GetLastChild());
+}
+
+TEST(Layers, Transform)
+{
+ TestContainerLayer layer(nullptr);
+
+ Matrix4x4 identity;
+ ASSERT_EQ(true, identity.IsIdentity());
+
+ ASSERT_EQ(identity, layer.GetTransform());
+}
+
+TEST(Layers, Type)
+{
+ TestContainerLayer layer(nullptr);
+ ASSERT_EQ(nullptr, layer.AsPaintedLayer());
+ ASSERT_EQ(nullptr, layer.AsRefLayer());
+ ASSERT_EQ(nullptr, layer.AsColorLayer());
+}
+
+TEST(Layers, UserData)
+{
+ UniquePtr<TestContainerLayer> layerPtr(new TestContainerLayer(nullptr));
+ TestContainerLayer& layer = *layerPtr;
+
+ void* key1 = (void*)1;
+ void* key2 = (void*)2;
+ void* key3 = (void*)3;
+
+ ASSERT_EQ(nullptr, layer.GetUserData(key1));
+ ASSERT_EQ(nullptr, layer.GetUserData(key2));
+ ASSERT_EQ(nullptr, layer.GetUserData(key3));
+
+ TestUserData* data1 = new TestUserData;
+ TestUserData* data2 = new TestUserData;
+ TestUserData* data3 = new TestUserData;
+
+ layer.SetUserData(key1, data1);
+ layer.SetUserData(key2, data2);
+ layer.SetUserData(key3, data3);
+
+ // Also checking that the user data is returned but not free'd
+ UniquePtr<LayerUserData> d1(layer.RemoveUserData(key1));
+ UniquePtr<LayerUserData> d2(layer.RemoveUserData(key2));
+ UniquePtr<LayerUserData> d3(layer.RemoveUserData(key3));
+ ASSERT_EQ(data1, d1.get());
+ ASSERT_EQ(data2, d2.get());
+ ASSERT_EQ(data3, d3.get());
+
+ layer.SetUserData(key1, d1.release());
+ layer.SetUserData(key2, d2.release());
+ layer.SetUserData(key3, d3.release());
+
+ // Layer has ownership of data1-3, check that they are destroyed
+ EXPECT_CALL(*data1, Die());
+ EXPECT_CALL(*data2, Die());
+ EXPECT_CALL(*data3, Die());
+}
+
+static already_AddRefed<Layer> CreateLayer(char aLayerType,
+ LayerManager* aManager) {
+ RefPtr<Layer> layer = nullptr;
+ if (aLayerType == 'c') {
+ layer = aManager->CreateContainerLayer();
+ } else if (aLayerType == 't') {
+ layer = aManager->CreatePaintedLayer();
+ } else if (aLayerType == 'o') {
+ layer = aManager->CreateColorLayer();
+ }
+ return layer.forget();
+}
+
+already_AddRefed<Layer> CreateLayerTree(const char* aLayerTreeDescription,
+ nsIntRegion* aVisibleRegions,
+ const Matrix4x4* aTransforms,
+ RefPtr<LayerManager>& manager,
+ nsTArray<RefPtr<Layer> >& aLayersOut) {
+ aLayersOut.Clear();
+
+ if (!manager) {
+ manager = new TestLayerManager();
+ }
+
+ RefPtr<Layer> rootLayer = nullptr;
+ RefPtr<ContainerLayer> parentContainerLayer = nullptr;
+ RefPtr<Layer> lastLayer = nullptr;
+ int layerNumber = 0;
+ for (size_t i = 0; i < strlen(aLayerTreeDescription); i++) {
+ if (aLayerTreeDescription[i] == '(') {
+ if (!lastLayer) {
+ printf(
+ "Syntax error, likely '(' character isn't preceded by a "
+ "container.\n");
+ MOZ_CRASH();
+ }
+ parentContainerLayer = lastLayer->AsContainerLayer();
+ if (!parentContainerLayer) {
+ printf("Layer before '(' must be a container.\n");
+ MOZ_CRASH();
+ }
+ } else if (aLayerTreeDescription[i] == ')') {
+ parentContainerLayer = parentContainerLayer->GetParent();
+ lastLayer = nullptr;
+ } else {
+ RefPtr<Layer> layer =
+ CreateLayer(aLayerTreeDescription[i], manager.get());
+ if (aVisibleRegions) {
+ layer->SetVisibleRegion(
+ LayerIntRegion::FromUnknownRegion(aVisibleRegions[layerNumber]));
+ layer->SetEventRegions(EventRegions(aVisibleRegions[layerNumber]));
+ }
+ if (aTransforms) {
+ layer->SetBaseTransform(aTransforms[layerNumber]);
+ }
+ aLayersOut.AppendElement(layer);
+ layerNumber++;
+ if (rootLayer && !parentContainerLayer) {
+ MOZ_CRASH();
+ }
+ if (!rootLayer) {
+ rootLayer = layer;
+ }
+ if (parentContainerLayer) {
+ parentContainerLayer->InsertAfter(layer,
+ parentContainerLayer->GetLastChild());
+ layer->SetParent(parentContainerLayer);
+ }
+ lastLayer = layer;
+ }
+ }
+ if (rootLayer) {
+ rootLayer->ComputeEffectiveTransforms(Matrix4x4());
+ manager->SetRoot(rootLayer);
+ if (rootLayer->AsHostLayer()) {
+ // Only perform this for LayerManagerComposite
+ CompositorBridgeParent::SetShadowProperties(rootLayer);
+ }
+ }
+ return rootLayer.forget();
+}
+
+TEST(Layers, LayerTree)
+{
+ const char* layerTreeSyntax = "c(c(tt))";
+ nsIntRegion layerVisibleRegion[] = {
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(0, 0, 100, 100)),
+ nsIntRegion(IntRect(10, 10, 20, 20)),
+ };
+ Matrix4x4 transforms[] = {
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4(),
+ Matrix4x4(),
+ };
+ nsTArray<RefPtr<Layer> > layers;
+
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root = CreateLayerTree(layerTreeSyntax, layerVisibleRegion,
+ transforms, lm, layers);
+
+ // B2G g++ doesn't like ASSERT_NE with nullptr directly. It thinks it's
+ // an int.
+ Layer* nullLayer = nullptr;
+ ASSERT_NE(nullLayer, layers[0]->AsContainerLayer());
+ ASSERT_NE(nullLayer, layers[1]->AsContainerLayer());
+ ASSERT_NE(nullLayer, layers[2]->AsPaintedLayer());
+ ASSERT_NE(nullLayer, layers[3]->AsPaintedLayer());
+}
+
+static void ValidateTreePointers(Layer* aLayer) {
+ if (aLayer->GetNextSibling()) {
+ ASSERT_EQ(aLayer, aLayer->GetNextSibling()->GetPrevSibling());
+ } else if (aLayer->GetParent()) {
+ ASSERT_EQ(aLayer, aLayer->GetParent()->GetLastChild());
+ }
+ if (aLayer->GetPrevSibling()) {
+ ASSERT_EQ(aLayer, aLayer->GetPrevSibling()->GetNextSibling());
+ } else if (aLayer->GetParent()) {
+ ASSERT_EQ(aLayer, aLayer->GetParent()->GetFirstChild());
+ }
+ if (aLayer->GetFirstChild()) {
+ ASSERT_EQ(aLayer, aLayer->GetFirstChild()->GetParent());
+ }
+ if (aLayer->GetLastChild()) {
+ ASSERT_EQ(aLayer, aLayer->GetLastChild()->GetParent());
+ }
+}
+
+static void ValidateTreePointers(nsTArray<RefPtr<Layer> >& aLayers) {
+ for (uint32_t i = 0; i < aLayers.Length(); i++) {
+ ValidateTreePointers(aLayers[i]);
+ }
+}
+
+TEST(Layers, RepositionChild)
+{
+ const char* layerTreeSyntax = "c(ttt)";
+
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root =
+ CreateLayerTree(layerTreeSyntax, nullptr, nullptr, lm, layers);
+ ContainerLayer* parent = root->AsContainerLayer();
+ ValidateTreePointers(layers);
+
+ // tree is currently like this (using indexes into layers):
+ // 0
+ // 1 2 3
+ ASSERT_EQ(layers[2], layers[1]->GetNextSibling());
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[3]->GetNextSibling());
+
+ parent->RepositionChild(layers[1], layers[3]);
+ ValidateTreePointers(layers);
+
+ // now the tree is like this:
+ // 0
+ // 2 3 1
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[3]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], layers[2]);
+ ValidateTreePointers(layers);
+
+ // no change
+ ASSERT_EQ(layers[3], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[3]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], layers[1]);
+ ValidateTreePointers(layers);
+
+ // 0
+ // 2 1 3
+ ASSERT_EQ(layers[1], layers[2]->GetNextSibling());
+ ASSERT_EQ(layers[3], layers[1]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[3]->GetNextSibling());
+
+ parent->RepositionChild(layers[3], nullptr);
+ ValidateTreePointers(layers);
+
+ // 0
+ // 3 2 1
+ ASSERT_EQ(layers[2], layers[3]->GetNextSibling());
+ ASSERT_EQ(layers[1], layers[2]->GetNextSibling());
+ ASSERT_EQ(nullptr, layers[1]->GetNextSibling());
+}
+
+class LayerMetricsWrapperTester : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // This ensures ScrollMetadata::sNullMetadata is initialized.
+ gfxPlatform::GetPlatform();
+ }
+};
+
+TEST_F(LayerMetricsWrapperTester, SimpleTree) {
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root =
+ CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers);
+ LayerMetricsWrapper wrapper(root);
+
+ ASSERT_EQ(root.get(), wrapper.GetLayer());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[1].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetFirstChild();
+ ASSERT_EQ(layers[3].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetFirstChild().IsValid());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(layers[4].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetLastChild();
+ ASSERT_EQ(layers[6].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+ LayerMetricsWrapper layer5 = wrapper;
+ wrapper = wrapper.GetPrevSibling();
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(layers[1].get(), wrapper.GetLayer());
+ ASSERT_TRUE(layer5 == wrapper.GetLastChild());
+ LayerMetricsWrapper rootWrapper(root);
+ ASSERT_TRUE(rootWrapper == wrapper.GetParent());
+}
+
+static ScrollMetadata MakeMetadata(ScrollableLayerGuid::ViewID aId) {
+ ScrollMetadata metadata;
+ metadata.GetMetrics().SetScrollId(aId);
+ return metadata;
+}
+
+TEST_F(LayerMetricsWrapperTester, MultiFramemetricsTree) {
+ nsTArray<RefPtr<Layer> > layers;
+ RefPtr<LayerManager> lm;
+ RefPtr<Layer> root =
+ CreateLayerTree("c(c(c(tt)c(t)))", nullptr, nullptr, lm, layers);
+
+ nsTArray<ScrollMetadata> metadata;
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID +
+ 0)); // topmost of root layer
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 1));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 2));
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(
+ ScrollableLayerGuid::NULL_SCROLL_ID)); // bottom of root layer
+ root->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 3));
+ layers[1]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(0,
+ MakeMetadata(ScrollableLayerGuid::NULL_SCROLL_ID));
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 4));
+ layers[2]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 5));
+ layers[4]->SetScrollMetadata(metadata);
+
+ metadata.Clear();
+ metadata.InsertElementAt(
+ 0, MakeMetadata(ScrollableLayerGuid::START_SCROLL_ID + 6));
+ layers[5]->SetScrollMetadata(metadata);
+
+ LayerMetricsWrapper wrapper(root, LayerMetricsWrapper::StartAt::TOP);
+ nsTArray<Layer*> expectedLayers;
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[0].get());
+ expectedLayers.AppendElement(layers[1].get());
+ expectedLayers.AppendElement(layers[2].get());
+ expectedLayers.AppendElement(layers[2].get());
+ expectedLayers.AppendElement(layers[3].get());
+ nsTArray<ScrollableLayerGuid::ViewID> expectedIds;
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 0);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 1);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 2);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 3);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ expectedIds.AppendElement(ScrollableLayerGuid::START_SCROLL_ID + 4);
+ expectedIds.AppendElement(ScrollableLayerGuid::NULL_SCROLL_ID);
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
+ ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetFirstChild();
+ }
+ ASSERT_FALSE(wrapper.IsValid());
+
+ wrapper = LayerMetricsWrapper(root, LayerMetricsWrapper::StartAt::BOTTOM);
+ for (int i = 5; i < 10; i++) {
+ ASSERT_EQ(expectedLayers[i], wrapper.GetLayer());
+ ASSERT_EQ(expectedIds[i], wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetFirstChild();
+ }
+ ASSERT_FALSE(wrapper.IsValid());
+
+ wrapper =
+ LayerMetricsWrapper(layers[4], LayerMetricsWrapper::StartAt::BOTTOM);
+ ASSERT_EQ(ScrollableLayerGuid::START_SCROLL_ID + 5,
+ wrapper.Metrics().GetScrollId());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(ScrollableLayerGuid::START_SCROLL_ID + 4,
+ wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ ASSERT_FALSE(wrapper.GetNextSibling().IsValid());
+ wrapper = wrapper.GetParent();
+ ASSERT_EQ(ScrollableLayerGuid::NULL_SCROLL_ID,
+ wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[2].get(), wrapper.GetLayer());
+ wrapper = wrapper.GetNextSibling();
+ ASSERT_EQ(ScrollableLayerGuid::START_SCROLL_ID + 6,
+ wrapper.Metrics().GetScrollId());
+ ASSERT_EQ(layers[5].get(), wrapper.GetLayer());
+}
diff --git a/gfx/tests/gtest/TestLayers.h b/gfx/tests/gtest/TestLayers.h
new file mode 100644
index 0000000000..9812a39ff5
--- /dev/null
+++ b/gfx/tests/gtest/TestLayers.h
@@ -0,0 +1,48 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#ifndef GFX_TEST_LAYERS_H
+#define GFX_TEST_LAYERS_H
+
+#include "Layers.h"
+#include "nsTArray.h"
+#include "mozilla/layers/ISurfaceAllocator.h"
+
+namespace mozilla {
+namespace layers {
+
+class TestSurfaceAllocator final : public ISurfaceAllocator {
+ public:
+ TestSurfaceAllocator() = default;
+ virtual ~TestSurfaceAllocator() = default;
+
+ bool IsSameProcess() const override { return true; }
+};
+
+} // namespace layers
+} // namespace mozilla
+
+/* Create layer tree from a simple layer tree description syntax.
+ * Each index is either the first letter of the layer type or
+ * a '(',')' to indicate the start/end of the child layers.
+ * The aim of this function is to remove hard to read
+ * layer tree creation code.
+ *
+ * Example "c(c(c(tt)t))" would yield:
+ * c
+ * |
+ * c
+ * / \
+ * c t
+ * / \
+ * t t
+ */
+already_AddRefed<mozilla::layers::Layer> CreateLayerTree(
+ const char* aLayerTreeDescription, nsIntRegion* aVisibleRegions,
+ const mozilla::gfx::Matrix4x4* aTransforms,
+ RefPtr<mozilla::layers::LayerManager>& aLayerManager,
+ nsTArray<RefPtr<mozilla::layers::Layer> >& aLayersOut);
+
+#endif
diff --git a/gfx/tests/gtest/TestMatrix.cpp b/gfx/tests/gtest/TestMatrix.cpp
new file mode 100644
index 0000000000..e55e8e793b
--- /dev/null
+++ b/gfx/tests/gtest/TestMatrix.cpp
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/gfx/Matrix.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+TEST(Matrix, TransformAndClipRect)
+{
+ Rect c(100, 100, 100, 100);
+ Matrix4x4 m;
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 50, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 250, 20, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 250, 20, 20), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 250, 100, 20), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 250, 100, 20), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 50, 20, 100), c).IsEmpty());
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(250, 150, 20, 100), c).IsEmpty());
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 100), c)
+ .IsEqualInterior(Rect(100, 100, 50, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 100), c)
+ .IsEqualInterior(Rect(150, 100, 50, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 150, 100, 100), c)
+ .IsEqualInterior(Rect(150, 150, 50, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 100, 100), c)
+ .IsEqualInterior(Rect(100, 150, 50, 50)));
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(110, 110, 80, 80), c)
+ .IsEqualInterior(Rect(110, 110, 80, 80)));
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 200, 200), c)
+ .IsEqualInterior(Rect(100, 100, 100, 100)));
+
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 200, 100), c)
+ .IsEqualInterior(Rect(100, 100, 100, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 150, 200, 100), c)
+ .IsEqualInterior(Rect(100, 150, 100, 50)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(50, 50, 100, 200), c)
+ .IsEqualInterior(Rect(100, 100, 50, 100)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 100, 200), c)
+ .IsEqualInterior(Rect(150, 100, 50, 100)));
+
+ Matrix4x4 m2 = Matrix4x4::From2D(Matrix(22.68, 0, 0, 12, 16, 164));
+ EXPECT_TRUE(
+ m2.TransformAndClipBounds(Rect(0, 0, 100, 100), Rect(1024, 1024, 0, 0))
+ .IsEmpty());
+
+ // Empty rectangles should still have meaningful corners.
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 50, 0, 200), c)
+ .IsEqualEdges(Rect(150, 100, 0, 100)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 150, 0, 0), c)
+ .IsEqualEdges(Rect(150, 150, 0, 0)));
+ EXPECT_TRUE(m.TransformAndClipBounds(Rect(150, 100, 300, 0), c)
+ .IsEqualEdges(Rect(150, 100, 50, 0)));
+}
+
+TEST(Matrix, RotateTransform)
+{
+ gfx::Matrix4x4 transformMatrix;
+ gfx::Point3D trans, scale;
+ gfx::Quaternion orient;
+
+ auto floor = [&](float aValue, int aDecimal) {
+ const int digit = pow(10, aDecimal);
+ const float result = (int)(aValue * digit);
+ return result / digit;
+ };
+
+ // Test rotate 45 degree on x-axis.
+ gfx::Quaternion expectedOrient(0.382f, 0.0f, 0.0f, 0.923f);
+ transformMatrix.RotateX(0.785f);
+ // the orient would be (x:0.3825, y:0, z:0, w: 0.9239)
+ transformMatrix.Decompose(trans, orient, scale);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+
+ // Test set rotate matrix from a quaternion and
+ // compare it with the result from decompose.
+ transformMatrix = gfx::Matrix4x4();
+ transformMatrix.SetRotationFromQuaternion(orient);
+ transformMatrix.Decompose(trans, orient, scale);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+
+ // Test rotate -45 degree on axis: (0.577f, 0.577f, 0.577f).
+ transformMatrix = gfx::Matrix4x4();
+ transformMatrix.SetRotateAxisAngle(0.577f, 0.577f, 0.577f, -0.785f);
+ // the orient would be (x:-0.2208, y:-0.2208, z:-0.2208, w: 0.9239)
+ transformMatrix.Decompose(trans, orient, scale);
+
+ expectedOrient.Set(-0.220f, -0.220f, -0.220f, 0.923f);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+
+ // Test set rotate matrix from a quaternion and
+ // compare it with the result from decompose.
+ transformMatrix = gfx::Matrix4x4();
+ transformMatrix.SetRotationFromQuaternion(orient);
+ transformMatrix.Decompose(trans, orient, scale);
+ EXPECT_EQ(floor(orient.x, 3), expectedOrient.x);
+ EXPECT_EQ(floor(orient.y, 3), expectedOrient.y);
+ EXPECT_EQ(floor(orient.z, 3), expectedOrient.z);
+ EXPECT_EQ(floor(orient.w, 3), expectedOrient.w);
+}
+
+TEST(Matrix4x4Flagged, Mult)
+{
+ Matrix4x4Flagged simple =
+ Matrix4x4::Translation(Point(42, 42)) * Matrix4x4::Scaling(3, 3, 1);
+ // For the general matrix, put a value in every field to make sure
+ // nothing gets dropped.
+ Matrix4x4 general(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2);
+
+ // Use Matrix4x4::operator*(Matrix4x4).
+ // For the purposes of this test, assume that's correct.
+ Matrix4x4Flagged realResult = Matrix4x4Flagged(simple.GetMatrix() * general);
+
+ // Check that Matrix4x4Flagged::operator*(Matrix4x4Flagged) produces the same
+ // result.
+ Matrix4x4Flagged flaggedResult = simple * Matrix4x4Flagged(general);
+ EXPECT_EQ(realResult, flaggedResult);
+
+ // Check that Matrix4x4Flagged::operator*(Matrix4x4) produces the same result.
+ Matrix4x4Flagged mixedResult = simple * general;
+ EXPECT_EQ(realResult, mixedResult);
+}
diff --git a/gfx/tests/gtest/TestMoz2D.cpp b/gfx/tests/gtest/TestMoz2D.cpp
new file mode 100644
index 0000000000..50854cdc98
--- /dev/null
+++ b/gfx/tests/gtest/TestMoz2D.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "TestBase.h"
+#include "TestPoint.h"
+#include "TestScaling.h"
+#include "TestBugs.h"
+
+TEST(Moz2D, Bugs)
+{
+ TestBugs* test = new TestBugs();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
+
+TEST(Moz2D, Point)
+{
+ TestBase* test = new TestPoint();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
+
+TEST(Moz2D, Scaling)
+{
+ TestBase* test = new TestScaling();
+ int failures = 0;
+ test->RunTests(&failures);
+ delete test;
+
+ ASSERT_EQ(failures, 0);
+}
diff --git a/gfx/tests/gtest/TestPolygon.cpp b/gfx/tests/gtest/TestPolygon.cpp
new file mode 100644
index 0000000000..168f7b46d9
--- /dev/null
+++ b/gfx/tests/gtest/TestPolygon.cpp
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "PolygonTestUtils.h"
+
+#include "nsTArray.h"
+#include "Point.h"
+#include "Polygon.h"
+#include "Triangle.h"
+
+using namespace mozilla::gfx;
+typedef mozilla::gfx::Polygon MozPolygon;
+
+TEST(MozPolygon, TriangulateRectangle)
+{
+ const MozPolygon p{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 1.0f, 1.0f), Point4D(1.0f, 0.0f, 1.0f, 1.0f)};
+
+ const nsTArray<Triangle> triangles = p.ToTriangles();
+ const nsTArray<Triangle> expected = {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(1.0f, 1.0f)),
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))};
+
+ AssertArrayEQ(triangles, expected);
+}
+
+TEST(MozPolygon, TriangulatePentagon)
+{
+ const MozPolygon p{
+ Point4D(0.0f, 0.0f, 1.0f, 1.0f), Point4D(0.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(0.5f, 1.5f, 1.0f, 1.0f), Point4D(1.0f, 1.0f, 1.0f, 1.0f),
+ Point4D(1.0f, 0.0f, 1.0f, 1.0f)};
+
+ const nsTArray<Triangle> triangles = p.ToTriangles();
+ const nsTArray<Triangle> expected = {
+ Triangle(Point(0.0f, 0.0f), Point(0.0f, 1.0f), Point(0.5f, 1.5f)),
+ Triangle(Point(0.0f, 0.0f), Point(0.5f, 1.5f), Point(1.0f, 1.0f)),
+ Triangle(Point(0.0f, 0.0f), Point(1.0f, 1.0f), Point(1.0f, 0.0f))};
+
+ AssertArrayEQ(triangles, expected);
+}
+
+static void TestClipRect(const MozPolygon& aPolygon,
+ const MozPolygon& aExpected, const Rect& aRect) {
+ const MozPolygon res = aPolygon.ClipPolygon(MozPolygon::FromRect(aRect));
+ EXPECT_TRUE(res == aExpected);
+}
+
+TEST(MozPolygon, ClipRectangle)
+{
+ MozPolygon polygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(0.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(1.0f, 0.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, polygon, Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ MozPolygon expected = MozPolygon{
+ Point4D(0.0f, 0.0f, 0.0f, 1.0f), Point4D(0.0f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f), Point4D(0.8f, 0.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
+
+ expected = MozPolygon{
+ Point4D(0.2f, 0.2f, 0.0f, 1.0f), Point4D(0.2f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f), Point4D(1.0f, 0.2f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
+
+ expected = MozPolygon{
+ Point4D(0.2f, 0.2f, 0.0f, 1.0f), Point4D(0.2f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f), Point4D(0.8f, 0.2f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
+}
+
+TEST(MozPolygon, ClipTriangle)
+{
+ MozPolygon clipped, expected;
+ const MozPolygon polygon{Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f)};
+
+ expected = MozPolygon{Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 1.0f, 1.0f));
+
+ expected = MozPolygon{Point4D(0.0f, 0.0f, 0.0f, 1.0f),
+ Point4D(0.0f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.0f, 0.0f, 0.8f, 0.8f));
+
+ expected = MozPolygon{Point4D(0.2f, 0.2f, 0.0f, 1.0f),
+ Point4D(0.2f, 1.0f, 0.0f, 1.0f),
+ Point4D(1.0f, 1.0f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.8f, 0.8f));
+
+ expected = MozPolygon{Point4D(0.2f, 0.2f, 0.0f, 1.0f),
+ Point4D(0.2f, 0.8f, 0.0f, 1.0f),
+ Point4D(0.8f, 0.8f, 0.0f, 1.0f)};
+ TestClipRect(polygon, expected, Rect(0.2f, 0.2f, 0.6f, 0.6f));
+}
diff --git a/gfx/tests/gtest/TestQcms.cpp b/gfx/tests/gtest/TestQcms.cpp
new file mode 100644
index 0000000000..54bb349ea8
--- /dev/null
+++ b/gfx/tests/gtest/TestQcms.cpp
@@ -0,0 +1,506 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gtest/MozGTestBench.h"
+#include "gmock/gmock.h"
+
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/SSE.h"
+#include "mozilla/arm.h"
+#include "qcms.h"
+#include "qcmsint.h"
+
+#include <cmath>
+
+/* SSEv1 is only included in non-Windows or non-x86-64-bit builds. */
+#if defined(MOZILLA_MAY_SUPPORT_SSE) && \
+ (!(defined(_MSC_VER) && defined(_M_AMD64)))
+# define QCMS_MAY_SUPPORT_SSE
+#endif
+
+using namespace mozilla;
+
+static bool CmpRgbChannel(const uint8_t* aRef, const uint8_t* aTest,
+ size_t aIndex) {
+ return std::abs(static_cast<int8_t>(aRef[aIndex] - aTest[aIndex])) <= 1;
+}
+
+template <bool kSwapRB, bool kHasAlpha>
+static bool CmpRgbBufferImpl(const uint8_t* aRefBuffer,
+ const uint8_t* aTestBuffer, size_t aPixels) {
+ const size_t pixelSize = kHasAlpha ? 4 : 3;
+ if (memcmp(aRefBuffer, aTestBuffer, aPixels * pixelSize) == 0) {
+ return true;
+ }
+
+ const size_t kRIndex = kSwapRB ? 2 : 0;
+ const size_t kGIndex = 1;
+ const size_t kBIndex = kSwapRB ? 0 : 2;
+ const size_t kAIndex = 3;
+
+ size_t remaining = aPixels;
+ const uint8_t* ref = aRefBuffer;
+ const uint8_t* test = aTestBuffer;
+ while (remaining > 0) {
+ if (!CmpRgbChannel(ref, test, kRIndex) ||
+ !CmpRgbChannel(ref, test, kGIndex) ||
+ !CmpRgbChannel(ref, test, kBIndex) ||
+ (kHasAlpha && ref[kAIndex] != test[kAIndex])) {
+ EXPECT_EQ(test[kRIndex], ref[kRIndex]);
+ EXPECT_EQ(test[kGIndex], ref[kGIndex]);
+ EXPECT_EQ(test[kBIndex], ref[kBIndex]);
+ if (kHasAlpha) {
+ EXPECT_EQ(test[kAIndex], ref[kAIndex]);
+ }
+ return false;
+ }
+
+ --remaining;
+ ref += pixelSize;
+ test += pixelSize;
+ }
+
+ return true;
+}
+
+template <bool kSwapRB, bool kHasAlpha>
+static size_t GetRgbInputBufferImpl(UniquePtr<uint8_t[]>& aOutBuffer) {
+ const uint8_t colorSamples[] = {0, 5, 16, 43, 101, 127, 182, 255};
+ const size_t colorSampleMax = sizeof(colorSamples) / sizeof(uint8_t);
+ const size_t pixelSize = kHasAlpha ? 4 : 3;
+ const size_t pixelCount = colorSampleMax * colorSampleMax * 256 * 3;
+
+ aOutBuffer = MakeUnique<uint8_t[]>(pixelCount * pixelSize);
+ if (!aOutBuffer) {
+ return 0;
+ }
+
+ const size_t kRIndex = kSwapRB ? 2 : 0;
+ const size_t kGIndex = 1;
+ const size_t kBIndex = kSwapRB ? 0 : 2;
+ const size_t kAIndex = 3;
+
+ // Sample every red pixel value with a subset of green and blue.
+ uint8_t* color = aOutBuffer.get();
+ for (uint16_t r = 0; r < 256; ++r) {
+ for (uint8_t g : colorSamples) {
+ for (uint8_t b : colorSamples) {
+ color[kRIndex] = r;
+ color[kGIndex] = g;
+ color[kBIndex] = b;
+ if (kHasAlpha) {
+ color[kAIndex] = 0x80;
+ }
+ color += pixelSize;
+ }
+ }
+ }
+
+ // Sample every green pixel value with a subset of red and blue.
+ for (uint8_t r : colorSamples) {
+ for (uint16_t g = 0; g < 256; ++g) {
+ for (uint8_t b : colorSamples) {
+ color[kRIndex] = r;
+ color[kGIndex] = g;
+ color[kBIndex] = b;
+ if (kHasAlpha) {
+ color[kAIndex] = 0x80;
+ }
+ color += pixelSize;
+ }
+ }
+ }
+
+ // Sample every blue pixel value with a subset of red and green.
+ for (uint8_t r : colorSamples) {
+ for (uint8_t g : colorSamples) {
+ for (uint16_t b = 0; b < 256; ++b) {
+ color[kRIndex] = r;
+ color[kGIndex] = g;
+ color[kBIndex] = b;
+ if (kHasAlpha) {
+ color[kAIndex] = 0x80;
+ }
+ color += pixelSize;
+ }
+ }
+ }
+
+ return pixelCount;
+}
+
+static size_t GetRgbInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
+ return GetRgbInputBufferImpl<false, false>(aOutBuffer);
+}
+
+static size_t GetRgbaInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
+ return GetRgbInputBufferImpl<false, true>(aOutBuffer);
+}
+
+static size_t GetBgraInputBuffer(UniquePtr<uint8_t[]>& aOutBuffer) {
+ return GetRgbInputBufferImpl<true, true>(aOutBuffer);
+}
+
+static bool CmpRgbBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
+ size_t aPixels) {
+ return CmpRgbBufferImpl<false, false>(aRefBuffer, aTestBuffer, aPixels);
+}
+
+static bool CmpRgbaBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
+ size_t aPixels) {
+ return CmpRgbBufferImpl<false, true>(aRefBuffer, aTestBuffer, aPixels);
+}
+
+static bool CmpBgraBuffer(const uint8_t* aRefBuffer, const uint8_t* aTestBuffer,
+ size_t aPixels) {
+ return CmpRgbBufferImpl<true, true>(aRefBuffer, aTestBuffer, aPixels);
+}
+
+static void ClearRgbBuffer(uint8_t* aBuffer, size_t aPixels) {
+ if (aBuffer) {
+ memset(aBuffer, 0, aPixels * 3);
+ }
+}
+
+static void ClearRgbaBuffer(uint8_t* aBuffer, size_t aPixels) {
+ if (aBuffer) {
+ memset(aBuffer, 0, aPixels * 4);
+ }
+}
+
+static UniquePtr<uint8_t[]> GetRgbOutputBuffer(size_t aPixels) {
+ UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 3);
+ ClearRgbBuffer(buffer.get(), aPixels);
+ return buffer;
+}
+
+static UniquePtr<uint8_t[]> GetRgbaOutputBuffer(size_t aPixels) {
+ UniquePtr<uint8_t[]> buffer = MakeUnique<uint8_t[]>(aPixels * 4);
+ ClearRgbaBuffer(buffer.get(), aPixels);
+ return buffer;
+}
+
+class GfxQcms_ProfilePairBase : public ::testing::Test {
+ protected:
+ GfxQcms_ProfilePairBase()
+ : mInProfile(nullptr),
+ mOutProfile(nullptr),
+ mTransform(nullptr),
+ mPixels(0),
+ mStorageType(QCMS_DATA_RGB_8),
+ mPrecache(false) {}
+
+ void TransformPrecache();
+ void TransformPrecachePlatformExt();
+
+ void SetUp() override {
+ // XXX: This means that we can't have qcms v2 unit test
+ // without changing the qcms API.
+ qcms_enable_iccv4();
+ }
+
+ void TearDown() override {
+ if (mInProfile) {
+ qcms_profile_release(mInProfile);
+ }
+ if (mOutProfile) {
+ qcms_profile_release(mOutProfile);
+ }
+ if (mTransform) {
+ qcms_transform_release(mTransform);
+ }
+ }
+
+ bool SetTransform(qcms_transform* aTransform) {
+ if (mTransform) {
+ qcms_transform_release(mTransform);
+ }
+ mTransform = aTransform;
+ return !!mTransform;
+ }
+
+ bool SetTransform(qcms_data_type aType) {
+ return SetTransform(qcms_transform_create(mInProfile, aType, mOutProfile,
+ aType, QCMS_INTENT_DEFAULT));
+ }
+
+ bool SetBuffers(qcms_data_type aType) {
+ switch (aType) {
+ case QCMS_DATA_RGB_8:
+ mPixels = GetRgbInputBuffer(mInput);
+ mRef = GetRgbOutputBuffer(mPixels);
+ mOutput = GetRgbOutputBuffer(mPixels);
+ break;
+ case QCMS_DATA_RGBA_8:
+ mPixels = GetRgbaInputBuffer(mInput);
+ mRef = GetRgbaOutputBuffer(mPixels);
+ mOutput = GetRgbaOutputBuffer(mPixels);
+ break;
+ case QCMS_DATA_BGRA_8:
+ mPixels = GetBgraInputBuffer(mInput);
+ mRef = GetRgbaOutputBuffer(mPixels);
+ mOutput = GetRgbaOutputBuffer(mPixels);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+
+ mStorageType = aType;
+ return mInput && mOutput && mRef && mPixels > 0u;
+ }
+
+ void ClearOutputBuffer() {
+ switch (mStorageType) {
+ case QCMS_DATA_RGB_8:
+ ClearRgbBuffer(mOutput.get(), mPixels);
+ break;
+ case QCMS_DATA_RGBA_8:
+ case QCMS_DATA_BGRA_8:
+ ClearRgbaBuffer(mOutput.get(), mPixels);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+ }
+
+ void ProduceRef(transform_fn_t aFn) {
+ aFn(mTransform, mInput.get(), mRef.get(), mPixels);
+ }
+
+ void CopyInputToRef() {
+ size_t pixelSize = 0;
+ switch (mStorageType) {
+ case QCMS_DATA_RGB_8:
+ pixelSize = 3;
+ break;
+ case QCMS_DATA_RGBA_8:
+ case QCMS_DATA_BGRA_8:
+ pixelSize = 4;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+
+ memcpy(mRef.get(), mInput.get(), mPixels * pixelSize);
+ }
+
+ void ProduceOutput(transform_fn_t aFn) {
+ ClearOutputBuffer();
+ aFn(mTransform, mInput.get(), mOutput.get(), mPixels);
+ }
+
+ bool VerifyOutput(const UniquePtr<uint8_t[]>& aBuf) {
+ switch (mStorageType) {
+ case QCMS_DATA_RGB_8:
+ return CmpRgbBuffer(aBuf.get(), mOutput.get(), mPixels);
+ case QCMS_DATA_RGBA_8:
+ return CmpRgbaBuffer(aBuf.get(), mOutput.get(), mPixels);
+ case QCMS_DATA_BGRA_8:
+ return CmpBgraBuffer(aBuf.get(), mOutput.get(), mPixels);
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unknown type!");
+ break;
+ }
+
+ return false;
+ }
+
+ bool ProduceVerifyOutput(transform_fn_t aFn) {
+ ProduceOutput(aFn);
+ return VerifyOutput(mRef);
+ }
+
+ void PrecacheOutput() {
+ qcms_profile_precache_output_transform(mOutProfile);
+ mPrecache = true;
+ }
+
+ qcms_profile* mInProfile;
+ qcms_profile* mOutProfile;
+ qcms_transform* mTransform;
+
+ UniquePtr<uint8_t[]> mInput;
+ UniquePtr<uint8_t[]> mOutput;
+ UniquePtr<uint8_t[]> mRef;
+ size_t mPixels;
+ qcms_data_type mStorageType;
+ bool mPrecache;
+};
+
+void GfxQcms_ProfilePairBase::TransformPrecache() {
+ // Produce reference using interpolation and the lookup tables.
+ ASSERT_FALSE(mPrecache);
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_RGB_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
+ ProduceRef(qcms_transform_data_rgb_out_lut);
+
+ // Produce output using lut and precaching.
+ PrecacheOutput();
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_precache));
+}
+
+void GfxQcms_ProfilePairBase::TransformPrecachePlatformExt() {
+ PrecacheOutput();
+
+ // Verify RGB transforms.
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_RGB_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGB_8));
+ ProduceRef(qcms_transform_data_rgb_out_lut_precache);
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_sse2));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_AVX
+ if (mozilla::supports_avx()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_avx));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_NEON
+ if (mozilla::supports_neon()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgb_out_lut_neon));
+ }
+#endif
+
+ // Verify RGBA transforms.
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_RGBA_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_RGBA_8));
+ ProduceRef(qcms_transform_data_rgba_out_lut_precache);
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_sse2));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_AVX
+ if (mozilla::supports_avx()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_avx));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_NEON
+ if (mozilla::supports_neon()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_rgba_out_lut_neon));
+ }
+#endif
+
+ // Verify BGRA transforms.
+ ASSERT_TRUE(SetBuffers(QCMS_DATA_BGRA_8));
+ ASSERT_TRUE(SetTransform(QCMS_DATA_BGRA_8));
+ ProduceRef(qcms_transform_data_bgra_out_lut_precache);
+#ifdef MOZILLA_MAY_SUPPORT_SSE2
+ if (mozilla::supports_sse2()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_sse2));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_AVX
+ if (mozilla::supports_avx()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_avx));
+ }
+#endif
+#ifdef MOZILLA_MAY_SUPPORT_NEON
+ if (mozilla::supports_neon()) {
+ EXPECT_TRUE(ProduceVerifyOutput(qcms_transform_data_bgra_out_lut_neon));
+ }
+#endif
+}
+
+class GfxQcms_sRGB_To_sRGB : public GfxQcms_ProfilePairBase {
+ protected:
+ void SetUp() override {
+ GfxQcms_ProfilePairBase::SetUp();
+ mInProfile = qcms_profile_sRGB();
+ mOutProfile = qcms_profile_sRGB();
+ }
+};
+
+class GfxQcms_sRGB_To_SamsungSyncmaster : public GfxQcms_ProfilePairBase {
+ protected:
+ void SetUp() override {
+ GfxQcms_ProfilePairBase::SetUp();
+ mInProfile = qcms_profile_sRGB();
+ mOutProfile = qcms_profile_from_path("lcms_samsung_syncmaster.icc");
+ }
+};
+
+class GfxQcms_sRGB_To_ThinkpadW540 : public GfxQcms_ProfilePairBase {
+ protected:
+ void SetUp() override {
+ GfxQcms_ProfilePairBase::SetUp();
+ mInProfile = qcms_profile_sRGB();
+ mOutProfile = qcms_profile_from_path("lcms_thinkpad_w540.icc");
+ }
+};
+
+#define TEST_QCMS_PROFILE_F(test_fixture) \
+ TEST_F(test_fixture, TransformPrecachePlatformExt) { \
+ GfxQcms_ProfilePairBase::TransformPrecachePlatformExt(); \
+ }
+
+TEST_F(GfxQcms_sRGB_To_sRGB, TransformPrecache) {
+ // TODO(aosmond): This doesn't pass for the non-identity transform. Should
+ // they produce the same results?
+ GfxQcms_ProfilePairBase::TransformPrecache();
+}
+
+TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_sRGB)
+
+TEST_F(GfxQcms_sRGB_To_sRGB, TransformIdentity) {
+ PrecacheOutput();
+ SetBuffers(QCMS_DATA_RGB_8);
+ SetTransform(QCMS_DATA_RGB_8);
+ qcms_transform_data(mTransform, mInput.get(), mOutput.get(), mPixels);
+ EXPECT_TRUE(VerifyOutput(mInput));
+}
+
+TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_SamsungSyncmaster)
+TEST_QCMS_PROFILE_F(GfxQcms_sRGB_To_ThinkpadW540)
+
+class GfxQcmsPerf_Base : public GfxQcms_sRGB_To_ThinkpadW540 {
+ protected:
+ explicit GfxQcmsPerf_Base(qcms_data_type aType) { mStorageType = aType; }
+
+ void TransformPerf() { ProduceRef(qcms_transform_data_rgb_out_lut_precache); }
+
+ void TransformPlatformPerf() {
+ qcms_transform_data(mTransform, mInput.get(), mRef.get(), mPixels);
+ }
+
+ void SetUp() override {
+ GfxQcms_sRGB_To_ThinkpadW540::SetUp();
+ PrecacheOutput();
+ SetBuffers(mStorageType);
+ SetTransform(mStorageType);
+ }
+};
+
+class GfxQcmsPerf_Rgb : public GfxQcmsPerf_Base {
+ protected:
+ GfxQcmsPerf_Rgb() : GfxQcmsPerf_Base(QCMS_DATA_RGB_8) {}
+};
+
+class GfxQcmsPerf_Rgba : public GfxQcmsPerf_Base {
+ protected:
+ GfxQcmsPerf_Rgba() : GfxQcmsPerf_Base(QCMS_DATA_RGBA_8) {}
+};
+
+class GfxQcmsPerf_Bgra : public GfxQcmsPerf_Base {
+ protected:
+ GfxQcmsPerf_Bgra() : GfxQcmsPerf_Base(QCMS_DATA_BGRA_8) {}
+};
+
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformC, [this] { TransformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgb, TransformPlatform,
+ [this] { TransformPlatformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformC, [this] { TransformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Rgba, TransformPlatform,
+ [this] { TransformPlatformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformC, [this] { TransformPerf(); });
+MOZ_GTEST_BENCH_F(GfxQcmsPerf_Bgra, TransformPlatform,
+ [this] { TransformPlatformPerf(); });
diff --git a/gfx/tests/gtest/TestRect.cpp b/gfx/tests/gtest/TestRect.cpp
new file mode 100644
index 0000000000..6c3d102884
--- /dev/null
+++ b/gfx/tests/gtest/TestRect.cpp
@@ -0,0 +1,645 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include <limits>
+
+#include "gtest/gtest.h"
+
+#include "gfxTypes.h"
+#include "nsRect.h"
+#include "gfxRect.h"
+#include "mozilla/gfx/Rect.h"
+#include "mozilla/gfx/RectAbsolute.h"
+#include "mozilla/WritingModes.h"
+#ifdef XP_WIN
+# include <windows.h>
+#endif
+
+using mozilla::gfx::IntRect;
+using mozilla::gfx::IntRectAbsolute;
+
+template <class RectType>
+static bool TestConstructors() {
+ // Create a rectangle
+ RectType rect1(10, 20, 30, 40);
+
+ // Make sure the rectangle was properly initialized
+ EXPECT_TRUE(rect1.IsEqualRect(10, 20, 30, 40) && rect1.IsEqualXY(10, 20) &&
+ rect1.IsEqualSize(30, 40))
+ << "[1] Make sure the rectangle was properly initialized with "
+ "constructor";
+
+ // Create a second rect using the copy constructor
+ RectType rect2(rect1);
+
+ // Make sure the rectangle was properly initialized
+ EXPECT_TRUE(rect2.IsEqualEdges(rect1) &&
+ rect2.IsEqualXY(rect1.X(), rect1.Y()) &&
+ rect2.IsEqualSize(rect1.Width(), rect1.Height()))
+ << "[2] Make sure the rectangle was properly initialized with copy "
+ "constructor";
+
+ EXPECT_TRUE(!rect1.IsEmpty() && !rect1.IsZeroArea() && rect1.IsFinite() &&
+ !rect2.IsEmpty() && !rect2.IsZeroArea() && rect2.IsFinite())
+ << "[3] These rectangles are not empty and are finite";
+
+ rect1.SetRect(1, 2, 30, 40);
+ EXPECT_TRUE(rect1.X() == 1 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 31 &&
+ rect1.YMost() == 42);
+
+ rect1.SetRectX(11, 50);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 2 && rect1.Width() == 50 &&
+ rect1.Height() == 40 && rect1.XMost() == 61 &&
+ rect1.YMost() == 42);
+
+ rect1.SetRectY(22, 60);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 22 && rect1.Width() == 50 &&
+ rect1.Height() == 60 && rect1.XMost() == 61 &&
+ rect1.YMost() == 82);
+
+ rect1.SetBox(1, 2, 31, 42);
+ EXPECT_TRUE(rect1.X() == 1 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 31 &&
+ rect1.YMost() == 42);
+
+ rect1.SetBoxX(11, 61);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 2 && rect1.Width() == 50 &&
+ rect1.Height() == 40 && rect1.XMost() == 61 &&
+ rect1.YMost() == 42);
+
+ rect1.SetBoxY(22, 82);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 22 && rect1.Width() == 50 &&
+ rect1.Height() == 60 && rect1.XMost() == 61 &&
+ rect1.YMost() == 82);
+
+ rect1.SetRect(1, 2, 30, 40);
+ EXPECT_TRUE(rect1.X() == 1 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 31 &&
+ rect1.YMost() == 42);
+
+ rect1.MoveByX(10);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 2 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 41 &&
+ rect1.YMost() == 42);
+
+ rect1.MoveByY(20);
+ EXPECT_TRUE(rect1.X() == 11 && rect1.Y() == 22 && rect1.Width() == 30 &&
+ rect1.Height() == 40 && rect1.XMost() == 41 &&
+ rect1.YMost() == 62);
+
+ return true;
+}
+
+template <class RectType>
+static bool TestEqualityOperator() {
+ RectType rect1(10, 20, 30, 40);
+ RectType rect2(rect1);
+
+ // Test the equality operator
+ EXPECT_TRUE(rect1 == rect2) << "[1] Test the equality operator";
+
+ EXPECT_FALSE(!rect1.IsEqualInterior(rect2))
+ << "[2] Test the inequality operator";
+
+ // Make sure that two empty rects are equal
+ rect1.SetEmpty();
+ rect2.SetEmpty();
+ EXPECT_TRUE(rect1 == rect2) << "[3] Make sure that two empty rects are equal";
+
+ return true;
+}
+
+template <class RectType>
+static bool TestContainment() {
+ RectType rect1(10, 10, 50, 50);
+
+ // Test the point containment methods
+ //
+
+ // Basic test of a point in the middle of the rect
+ EXPECT_TRUE(rect1.Contains(rect1.Center()) &&
+ rect1.ContainsX(rect1.Center().x) &&
+ rect1.ContainsY(rect1.Center().y))
+ << "[1] Basic test of a point in the middle of the rect";
+
+ // Test against a point at the left/top edges
+ EXPECT_TRUE(rect1.Contains(rect1.X(), rect1.Y()) &&
+ rect1.ContainsX(rect1.X()) && rect1.ContainsY(rect1.Y()))
+ << "[2] Test against a point at the left/top edges";
+
+ // Test against a point at the right/bottom extents
+ EXPECT_FALSE(rect1.Contains(rect1.XMost(), rect1.YMost()) ||
+ rect1.ContainsX(rect1.XMost()) || rect1.ContainsY(rect1.YMost()))
+ << "[3] Test against a point at the right/bottom extents";
+
+ // Test the rect containment methods
+ //
+ RectType rect2(rect1);
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!rect1.Contains(rect2))
+ << "[4] Test against a rect that's the same as rect1";
+
+ // Test against a rect whose left edge (only) is outside of rect1
+ rect2.MoveByX(-1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[5] Test against a rect whose left edge (only) is outside of rect1";
+ rect2.MoveByX(1);
+
+ // Test against a rect whose top edge (only) is outside of rect1
+ rect2.MoveByY(-1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[6] Test against a rect whose top edge (only) is outside of rect1";
+ rect2.MoveByY(1);
+
+ // Test against a rect whose right edge (only) is outside of rect1
+ rect2.MoveByX(1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[7] Test against a rect whose right edge (only) is outside of rect1";
+ rect2.MoveByX(-1);
+
+ // Test against a rect whose bottom edge (only) is outside of rect1
+ rect2.MoveByY(1);
+ EXPECT_FALSE(rect1.Contains(rect2))
+ << "[8] Test against a rect whose bottom edge (only) is outside of rect1";
+ rect2.MoveByY(-1);
+
+ return true;
+}
+
+// Test the method that returns a boolean result but doesn't return a
+// a rectangle
+template <class RectType>
+static bool TestIntersects() {
+ RectType rect1(10, 10, 50, 50);
+ RectType rect2(rect1);
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[1] Test against a rect that's the same as rect1";
+
+ // Test against a rect that's enclosed by rect1
+ rect2.Inflate(-1, -1);
+ EXPECT_FALSE(!rect1.Contains(rect2) || !rect1.Intersects(rect2))
+ << "[2] Test against a rect that's enclosed by rect1";
+ rect2.Inflate(1, 1);
+
+ // Make sure inflate and deflate worked correctly
+ EXPECT_TRUE(rect1.IsEqualInterior(rect2))
+ << "[3] Make sure inflate and deflate worked correctly";
+
+ // Test against a rect that overlaps the left edge of rect1
+ rect2.MoveByX(-1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[4] Test against a rect that overlaps the left edge of rect1";
+ rect2.MoveByX(1);
+
+ // Test against a rect that's outside of rect1 on the left
+ rect2.MoveByX(-rect2.Width());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[5] Test against a rect that's outside of rect1 on the left";
+ rect2.MoveByX(rect2.Width());
+
+ // Test against a rect that overlaps the top edge of rect1
+ rect2.MoveByY(-1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[6] Test against a rect that overlaps the top edge of rect1";
+ rect2.MoveByY(1);
+
+ // Test against a rect that's outside of rect1 on the top
+ rect2.MoveByY(-rect2.Height());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[7] Test against a rect that's outside of rect1 on the top";
+ rect2.MoveByY(rect2.Height());
+
+ // Test against a rect that overlaps the right edge of rect1
+ rect2.MoveByX(1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[8] Test against a rect that overlaps the right edge of rect1";
+ rect2.MoveByX(-1);
+
+ // Test against a rect that's outside of rect1 on the right
+ rect2.MoveByX(rect2.Width());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[9] Test against a rect that's outside of rect1 on the right";
+ rect2.MoveByX(-rect2.Width());
+
+ // Test against a rect that overlaps the bottom edge of rect1
+ rect2.MoveByY(1);
+ EXPECT_FALSE(!rect1.Intersects(rect2))
+ << "[10] Test against a rect that overlaps the bottom edge of rect1";
+ rect2.MoveByY(-1);
+
+ // Test against a rect that's outside of rect1 on the bottom
+ rect2.MoveByY(rect2.Height());
+ EXPECT_FALSE(rect1.Intersects(rect2))
+ << "[11] Test against a rect that's outside of rect1 on the bottom";
+ rect2.MoveByY(-rect2.Height());
+
+ return true;
+}
+
+// Test the method that returns a boolean result and an intersection rect
+template <class RectType>
+static bool TestIntersection() {
+ RectType rect1(10, 10, 50, 50);
+ RectType rect2(rect1);
+ RectType dest;
+
+ // Test against a rect that's the same as rect1
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(rect1)))
+ << "[1] Test against a rect that's the same as rect1";
+
+ // Test against a rect that's enclosed by rect1
+ rect2.Inflate(-1, -1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(rect2)))
+ << "[2] Test against a rect that's enclosed by rect1";
+ rect2.Inflate(1, 1);
+
+ // Test against a rect that overlaps the left edge of rect1
+ rect2.MoveByX(-1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(
+ rect1.X(), rect1.Y(), rect1.Width() - 1, rect1.Height()))))
+ << "[3] Test against a rect that overlaps the left edge of rect1";
+ rect2.MoveByX(1);
+
+ // Test against a rect that's outside of rect1 on the left
+ rect2.MoveByX(-rect2.Width());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[4] Test against a rect that's outside of rect1 on the left";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[4] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[4b] Should be finite";
+ rect2.MoveByX(rect2.Width());
+
+ // Test against a rect that overlaps the top edge of rect1
+ rect2.MoveByY(-1);
+ EXPECT_FALSE(!dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(
+ rect1.X(), rect1.Y(), rect1.Width(), rect1.Height() - 1))))
+ << "[5] Test against a rect that overlaps the top edge of rect1";
+ EXPECT_TRUE(dest.IsFinite()) << "[5b] Should be finite";
+ rect2.MoveByY(1);
+
+ // Test against a rect that's outside of rect1 on the top
+ rect2.MoveByY(-rect2.Height());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[6] Test against a rect that's outside of rect1 on the top";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[6] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[6b] Should be finite";
+ rect2.MoveByY(rect2.Height());
+
+ // Test against a rect that overlaps the right edge of rect1
+ rect2.MoveByX(1);
+ EXPECT_FALSE(
+ !dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.X() + 1, rect1.Y(),
+ rect1.Width() - 1, rect1.Height()))))
+ << "[7] Test against a rect that overlaps the right edge of rect1";
+ rect2.MoveByX(-1);
+
+ // Test against a rect that's outside of rect1 on the right
+ rect2.MoveByX(rect2.Width());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[8] Test against a rect that's outside of rect1 on the right";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[8] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[8b] Should be finite";
+ rect2.MoveByX(-rect2.Width());
+
+ // Test against a rect that overlaps the bottom edge of rect1
+ rect2.MoveByY(1);
+ EXPECT_FALSE(
+ !dest.IntersectRect(rect1, rect2) ||
+ !(dest.IsEqualInterior(RectType(rect1.X(), rect1.Y() + 1, rect1.Width(),
+ rect1.Height() - 1))))
+ << "[9] Test against a rect that overlaps the bottom edge of rect1";
+ EXPECT_TRUE(dest.IsFinite()) << "[9b] Should be finite";
+ rect2.MoveByY(-1);
+
+ // Test against a rect that's outside of rect1 on the bottom
+ rect2.MoveByY(rect2.Height());
+ EXPECT_FALSE(dest.IntersectRect(rect1, rect2))
+ << "[10] Test against a rect that's outside of rect1 on the bottom";
+ // Make sure an empty rect is returned
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[10] Make sure an empty rect is returned";
+ EXPECT_TRUE(dest.IsFinite()) << "[10b] Should be finite";
+ rect2.MoveByY(-rect2.Height());
+
+ // Test against a rect with zero width or height
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(150, 100, 0, 100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[11] Intersection of rects with zero width or height should be empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[11b] Should be finite";
+
+ // Tests against a rect with negative width or height
+ //
+
+ // Test against a rect with negative width
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(100, 100, -100, 100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[12] Intersection of rects with negative width or height should be "
+ "empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[12b] Should be finite";
+
+ // Those two rects exactly overlap in some way...
+ // but we still want to return an empty rect
+ rect1.SetRect(100, 100, 100, 100);
+ rect2.SetRect(200, 200, -100, -100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[13] Intersection of rects with negative width or height should be "
+ "empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[13b] Should be finite";
+
+ // Test against two identical rects with negative height
+ rect1.SetRect(100, 100, 100, -100);
+ rect2.SetRect(100, 100, 100, -100);
+ EXPECT_TRUE(!dest.IntersectRect(rect1, rect2) && dest.IsEmpty() &&
+ dest.IsZeroArea())
+ << "[14] Intersection of rects with negative width or height should be "
+ "empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[14b] Should be finite";
+
+ return true;
+}
+
+template <class RectType>
+static bool TestUnion() {
+ RectType rect1;
+ RectType rect2(10, 10, 50, 50);
+ RectType dest;
+
+ // Check the case where the receiver is an empty rect
+ rect1.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ dest.IsEqualInterior(rect2))
+ << "[1] Check the case where the receiver is an empty rect";
+ EXPECT_TRUE(dest.IsFinite()) << "[1b] Should be finite";
+
+ // Check the case where the source rect is an empty rect
+ rect1 = rect2;
+ rect2.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ dest.IsEqualInterior(rect1))
+ << "[2] Check the case where the source rect is an empty rect";
+ EXPECT_TRUE(dest.IsFinite()) << "[2b] Should be finite";
+
+ // Test the case where both rects are empty
+ rect1.SetEmpty();
+ rect2.SetEmpty();
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(dest.IsEmpty() && dest.IsZeroArea())
+ << "[3] Test the case where both rects are empty";
+ EXPECT_TRUE(dest.IsFinite()) << "[3b] Should be finite";
+
+ // Test union case where the two rects don't overlap at all
+ rect1.SetRect(10, 10, 50, 50);
+ rect2.SetRect(100, 100, 50, 50);
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ (dest.IsEqualInterior(RectType(rect1.X(), rect1.Y(),
+ rect2.XMost() - rect1.X(),
+ rect2.YMost() - rect1.Y()))))
+ << "[4] Test union case where the two rects don't overlap at all";
+ EXPECT_TRUE(dest.IsFinite()) << "[4b] Should be finite";
+
+ // Test union case where the two rects overlap
+ rect1.SetRect(30, 30, 50, 50);
+ rect2.SetRect(10, 10, 50, 50);
+ dest.UnionRect(rect1, rect2);
+ EXPECT_TRUE(!dest.IsEmpty() && !dest.IsZeroArea() &&
+ (dest.IsEqualInterior(RectType(rect2.X(), rect2.Y(),
+ rect1.XMost() - rect2.X(),
+ rect1.YMost() - rect2.Y()))))
+ << "[5] Test union case where the two rects overlap";
+ EXPECT_TRUE(dest.IsFinite()) << "[5b] Should be finite";
+
+ return true;
+}
+
+static bool TestFiniteGfx() {
+ float posInf = std::numeric_limits<float>::infinity();
+ float negInf = -std::numeric_limits<float>::infinity();
+ float justNaN = std::numeric_limits<float>::quiet_NaN();
+
+ gfxFloat values[4] = {5.0, 10.0, 15.0, 20.0};
+
+ // Try the "non-finite" values for x, y, width, height, one at a time
+ for (int i = 0; i < 4; i += 1) {
+ values[i] = posInf;
+ gfxRect rectPosInf(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectPosInf.IsFinite())
+ << "For +inf (" << values[0] << "," << values[1] << "," << values[2]
+ << "," << values[3] << ")";
+
+ values[i] = negInf;
+ gfxRect rectNegInf(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectNegInf.IsFinite())
+ << "For -inf (" << values[0] << "," << values[1] << "," << values[2]
+ << "," << values[3] << ")";
+
+ values[i] = justNaN;
+ gfxRect rectNaN(values[0], values[1], values[2], values[3]);
+ EXPECT_FALSE(rectNaN.IsFinite())
+ << "For NaN (" << values[0] << "," << values[1] << "," << values[2]
+ << "," << values[3] << ")";
+
+ // Reset to a finite value...
+ values[i] = 5.0 * i;
+ }
+
+ return true;
+}
+
+// We want to test nsRect values that are still in range but where
+// the implementation is at risk of overflowing
+template <class RectType>
+static bool TestBug1135677() {
+ RectType rect1(1073741344, 1073741344, 1073756696, 1073819936);
+ RectType rect2(1073741820, 1073741820, 14400, 77640);
+ RectType dest;
+
+ dest = rect1.Intersect(rect2);
+
+ EXPECT_TRUE(dest.IsEqualRect(1073741820, 1073741820, 14400, 77640))
+ << "[1] Operation should not overflow internally.";
+
+ return true;
+}
+
+template <class RectType>
+static bool TestSetWH() {
+ RectType rect(1, 2, 3, 4);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 3, 4));
+ rect.SetWidth(13);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 13, 4));
+ rect.SetHeight(14);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 13, 14));
+ rect.SizeTo(23, 24);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 23, 24));
+ return true;
+}
+
+template <class RectType>
+static bool TestSwap() {
+ RectType rect(1, 2, 3, 4);
+ EXPECT_TRUE(rect.IsEqualRect(1, 2, 3, 4));
+ rect.Swap();
+ EXPECT_TRUE(rect.IsEqualRect(2, 1, 4, 3));
+ return true;
+}
+
+static void TestIntersectionLogicalHelper(nscoord x1, nscoord y1, nscoord w1,
+ nscoord h1, nscoord x2, nscoord y2,
+ nscoord w2, nscoord h2, nscoord xR,
+ nscoord yR, nscoord wR, nscoord hR,
+ bool isNonEmpty) {
+ nsRect rect1(x1, y1, w1, h1);
+ nsRect rect2(x2, y2, w2, h2);
+ nsRect rectDebug;
+ EXPECT_TRUE(isNonEmpty == rectDebug.IntersectRect(rect1, rect2));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(xR, yR, wR, hR)));
+
+ mozilla::LogicalRect r1(mozilla::WritingMode(), rect1.X(), rect1.Y(),
+ rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r2(mozilla::WritingMode(), rect2.X(), rect2.Y(),
+ rect2.Width(), rect2.Height());
+ EXPECT_TRUE(isNonEmpty == r1.IntersectRect(r1, r2));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(
+ r1.IStart(mozilla::WritingMode()), r1.BStart(mozilla::WritingMode()),
+ r1.ISize(mozilla::WritingMode()), r1.BSize(mozilla::WritingMode()))));
+
+ mozilla::LogicalRect r3(mozilla::WritingMode(), rect1.X(), rect1.Y(),
+ rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r4(mozilla::WritingMode(), rect2.X(), rect2.Y(),
+ rect2.Width(), rect2.Height());
+ EXPECT_TRUE(isNonEmpty == r4.IntersectRect(r3, r4));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(
+ r4.IStart(mozilla::WritingMode()), r4.BStart(mozilla::WritingMode()),
+ r4.ISize(mozilla::WritingMode()), r4.BSize(mozilla::WritingMode()))));
+
+ mozilla::LogicalRect r5(mozilla::WritingMode(), rect1.X(), rect1.Y(),
+ rect1.Width(), rect1.Height());
+ mozilla::LogicalRect r6(mozilla::WritingMode(), rect2.X(), rect2.Y(),
+ rect2.Width(), rect2.Height());
+ mozilla::LogicalRect r7(mozilla::WritingMode(), 0, 0, 1, 1);
+ EXPECT_TRUE(isNonEmpty == r7.IntersectRect(r5, r6));
+ EXPECT_TRUE(rectDebug.IsEqualEdges(nsRect(
+ r7.IStart(mozilla::WritingMode()), r7.BStart(mozilla::WritingMode()),
+ r7.ISize(mozilla::WritingMode()), r7.BSize(mozilla::WritingMode()))));
+}
+
+static void TestIntersectionLogical(nscoord x1, nscoord y1, nscoord w1,
+ nscoord h1, nscoord x2, nscoord y2,
+ nscoord w2, nscoord h2, nscoord xR,
+ nscoord yR, nscoord wR, nscoord hR,
+ bool isNonEmpty) {
+ TestIntersectionLogicalHelper(x1, y1, w1, h1, x2, y2, w2, h2, xR, yR, wR, hR,
+ isNonEmpty);
+ TestIntersectionLogicalHelper(x2, y2, w2, h2, x1, y1, w1, h1, xR, yR, wR, hR,
+ isNonEmpty);
+}
+
+TEST(Gfx, Logical)
+{
+ TestIntersectionLogical(578, 0, 2650, 1152, 1036, 0, 2312, 1, 1036, 0, 2192,
+ 1, true);
+ TestIntersectionLogical(0, 0, 1000, 1000, 500, 500, 1000, 1000, 500, 500, 500,
+ 500, true);
+ TestIntersectionLogical(100, 200, 300, 400, 50, 250, 100, 100, 100, 250, 50,
+ 100, true);
+ TestIntersectionLogical(0, 100, 200, 300, 300, 100, 100, 300, 300, 100, 0, 0,
+ false);
+}
+
+TEST(Gfx, nsRect)
+{
+ TestConstructors<nsRect>();
+ TestEqualityOperator<nsRect>();
+ TestContainment<nsRect>();
+ TestIntersects<nsRect>();
+ TestIntersection<nsRect>();
+ TestUnion<nsRect>();
+ TestBug1135677<nsRect>();
+ TestSetWH<nsRect>();
+ TestSwap<nsRect>();
+}
+
+TEST(Gfx, nsIntRect)
+{
+ TestConstructors<nsIntRect>();
+ TestEqualityOperator<nsIntRect>();
+ TestContainment<nsIntRect>();
+ TestIntersects<nsIntRect>();
+ TestIntersection<nsIntRect>();
+ TestUnion<nsIntRect>();
+ TestBug1135677<nsIntRect>();
+ TestSetWH<nsIntRect>();
+ TestSwap<nsIntRect>();
+}
+
+TEST(Gfx, gfxRect)
+{
+ TestConstructors<gfxRect>();
+ // Skip TestEqualityOperator<gfxRect>(); as gfxRect::operator== is private
+ TestContainment<gfxRect>();
+ TestIntersects<gfxRect>();
+ TestIntersection<gfxRect>();
+ TestUnion<gfxRect>();
+ TestBug1135677<gfxRect>();
+ TestFiniteGfx();
+ TestSetWH<gfxRect>();
+ TestSwap<gfxRect>();
+}
+
+static void TestMoveInsideAndClamp(IntRect aSrc, IntRect aTarget,
+ IntRect aExpected) {
+ // Test the implementation in BaseRect (x/y/width/height representation)
+ IntRect result = aSrc.MoveInsideAndClamp(aTarget);
+ EXPECT_TRUE(result.IsEqualEdges(aExpected))
+ << "Source " << aSrc << " Target " << aTarget << " Expected " << aExpected
+ << " Actual " << result;
+
+ // Also test the implementation in RectAbsolute (left/top/right/bottom
+ // representation)
+ IntRectAbsolute absSrc = IntRectAbsolute::FromRect(aSrc);
+ IntRectAbsolute absTarget = IntRectAbsolute::FromRect(aTarget);
+ IntRectAbsolute absExpected = IntRectAbsolute::FromRect(aExpected);
+
+ IntRectAbsolute absResult = absSrc.MoveInsideAndClamp(absTarget);
+ EXPECT_TRUE(absResult.IsEqualEdges(absExpected))
+ << "AbsSource " << absSrc << " AbsTarget " << absTarget << " AbsExpected "
+ << absExpected << " AbsActual " << absResult;
+}
+
+TEST(Gfx, MoveInsideAndClamp)
+{
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(1, -1, 10, 10),
+ IntRect(1, -1, 10, 10));
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(-1, -1, 12, 5),
+ IntRect(0, -1, 10, 5));
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(10, 11, 10, 0),
+ IntRect(10, 11, 10, 0));
+ TestMoveInsideAndClamp(IntRect(0, 0, 10, 10), IntRect(-10, -1, 10, 0),
+ IntRect(-10, -1, 10, 0));
+ TestMoveInsideAndClamp(IntRect(0, 0, 0, 0), IntRect(10, -10, 10, 10),
+ IntRect(10, 0, 0, 0));
+}
diff --git a/gfx/tests/gtest/TestRegion.cpp b/gfx/tests/gtest/TestRegion.cpp
new file mode 100644
index 0000000000..23ac24f51e
--- /dev/null
+++ b/gfx/tests/gtest/TestRegion.cpp
@@ -0,0 +1,1533 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <algorithm>
+
+#include "gtest/gtest.h"
+#include "nsRegion.h"
+#include "RegionBuilder.h"
+#include "mozilla/gfx/TiledRegion.h"
+#include "mozilla/UniquePtr.h"
+
+using namespace mozilla::gfx;
+
+//#define REGION_RANDOM_STRESS_TESTS
+
+class TestLargestRegion {
+ public:
+ static void TestSingleRect(nsRect r) {
+ nsRegion region(r);
+ EXPECT_TRUE(region.GetLargestRectangle().IsEqualInterior(r));
+ }
+ // Construct a rectangle, remove part of it, then check the remainder
+ static void TestNonRectangular() {
+ nsRegion r(nsRect(0, 0, 30, 30));
+
+ const int nTests = 19;
+ struct {
+ nsRect rect;
+ int64_t expectedArea;
+ } tests[nTests] = {// Remove a 20x10 chunk from the square
+ {nsRect(0, 0, 20, 10), 600},
+ {nsRect(10, 0, 20, 10), 600},
+ {nsRect(10, 20, 20, 10), 600},
+ {nsRect(0, 20, 20, 10), 600},
+ // Remove a 10x20 chunk from the square
+ {nsRect(0, 0, 10, 20), 600},
+ {nsRect(20, 0, 10, 20), 600},
+ {nsRect(20, 10, 10, 20), 600},
+ {nsRect(0, 10, 10, 20), 600},
+ // Remove the center 10x10
+ {nsRect(10, 10, 10, 10), 300},
+ // Remove the middle column
+ {nsRect(10, 0, 10, 30), 300},
+ // Remove the middle row
+ {nsRect(0, 10, 30, 10), 300},
+ // Remove the corners 10x10
+ {nsRect(0, 0, 10, 10), 600},
+ {nsRect(20, 20, 10, 10), 600},
+ {nsRect(20, 0, 10, 10), 600},
+ {nsRect(0, 20, 10, 10), 600},
+ // Remove the corners 20x20
+ {nsRect(0, 0, 20, 20), 300},
+ {nsRect(10, 10, 20, 20), 300},
+ {nsRect(10, 0, 20, 20), 300},
+ {nsRect(0, 10, 20, 20), 300}};
+
+ for (int32_t i = 0; i < nTests; i++) {
+ nsRegion r2;
+ r2.Sub(r, tests[i].rect);
+
+ EXPECT_TRUE(r2.IsComplex()) << "nsRegion code got unexpectedly smarter!";
+
+ nsRect largest = r2.GetLargestRectangle();
+ EXPECT_TRUE(largest.Width() * largest.Height() == tests[i].expectedArea)
+ << "Did not successfully find largest rectangle in non-rectangular "
+ "region on iteration "
+ << i;
+ }
+ }
+ static void TwoRectTest() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ const int nTests = 4;
+ struct {
+ nsRect rect1, rect2;
+ int64_t expectedArea;
+ } tests[nTests] = {
+ {nsRect(0, 0, 75, 40), nsRect(0, 60, 75, 40), 2500},
+ {nsRect(25, 0, 75, 40), nsRect(25, 60, 75, 40), 2500},
+ {nsRect(25, 0, 75, 40), nsRect(0, 60, 75, 40), 2000},
+ {nsRect(0, 0, 75, 40), nsRect(25, 60, 75, 40), 2000},
+ };
+ for (int32_t i = 0; i < nTests; i++) {
+ nsRegion r2;
+
+ r2.Sub(r, tests[i].rect1);
+ r2.Sub(r2, tests[i].rect2);
+
+ EXPECT_TRUE(r2.IsComplex()) << "nsRegion code got unexpectedly smarter!";
+
+ nsRect largest = r2.GetLargestRectangle();
+ EXPECT_TRUE(largest.Width() * largest.Height() == tests[i].expectedArea)
+ << "Did not successfully find largest rectangle in two-rect-subtract "
+ "region on iteration "
+ << i;
+ }
+ }
+ static void TestContainsSpecifiedRect() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.Or(r, nsRect(0, 300, 50, 50));
+ EXPECT_TRUE(r.GetLargestRectangle(nsRect(0, 300, 10, 10))
+ .IsEqualInterior(nsRect(0, 300, 50, 50)))
+ << "Chose wrong rectangle";
+ }
+ static void TestContainsSpecifiedOverflowingRect() {
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.Or(r, nsRect(0, 300, 50, 50));
+ EXPECT_TRUE(r.GetLargestRectangle(nsRect(0, 290, 10, 20))
+ .IsEqualInterior(nsRect(0, 300, 50, 50)))
+ << "Chose wrong rectangle";
+ }
+};
+
+TEST(Gfx, RegionSingleRect)
+{
+ TestLargestRegion::TestSingleRect(nsRect(0, 52, 720, 480));
+ TestLargestRegion::TestSingleRect(nsRect(-20, 40, 50, 20));
+ TestLargestRegion::TestSingleRect(nsRect(-20, 40, 10, 8));
+ TestLargestRegion::TestSingleRect(nsRect(-20, -40, 10, 8));
+ TestLargestRegion::TestSingleRect(nsRect(-10, -10, 20, 20));
+}
+
+TEST(Gfx, RegionNonRectangular)
+{ TestLargestRegion::TestNonRectangular(); }
+
+TEST(Gfx, RegionTwoRectTest)
+{ TestLargestRegion::TwoRectTest(); }
+
+TEST(Gfx, RegionContainsSpecifiedRect)
+{ TestLargestRegion::TestContainsSpecifiedRect(); }
+
+TEST(Gfx, RegionTestContainsSpecifiedOverflowingRect)
+{ TestLargestRegion::TestContainsSpecifiedOverflowingRect(); }
+
+TEST(Gfx, RegionScaleToInside)
+{
+ { // no rectangles
+ nsRegion r;
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result;
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+
+ { // one rectangle
+ nsRegion r(nsRect(0, 44760, 19096, 264));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0, 746, 318, 4));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+
+ { // the first rectangle gets adjusted
+ nsRegion r(nsRect(0, 44760, 19096, 264));
+ r.Or(r, nsRect(0, 45024, 19360, 1056));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0, 746, 318, 5));
+ result.Or(result, mozilla::gfx::IntRect(0, 751, 322, 17));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+
+ { // the second rectangle gets adjusted
+ nsRegion r(nsRect(0, 44760, 19360, 264));
+ r.Or(r, nsRect(0, 45024, 19096, 1056));
+
+ nsIntRegion scaled = r.ScaleToInsidePixels(1, 1, 60);
+ nsIntRegion result(mozilla::gfx::IntRect(0, 746, 322, 4));
+ result.Or(result, mozilla::gfx::IntRect(0, 750, 318, 18));
+
+ EXPECT_TRUE(result.IsEqual(scaled)) << "scaled result incorrect";
+ }
+}
+
+TEST(Gfx, RegionIsEqual)
+{
+ {
+ nsRegion r(nsRect(0, 0, 50, 50));
+ EXPECT_FALSE(nsRegion().IsEqual(r));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ EXPECT_TRUE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ nsRegion r2(nsRect(0, 0, 60, 50));
+ EXPECT_FALSE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ r1.OrWith(nsRect(0, 60, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ r2.OrWith(nsRect(0, 60, 50, 50));
+ EXPECT_TRUE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ r1.OrWith(nsRect(0, 60, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ r2.OrWith(nsRect(0, 70, 50, 50));
+ EXPECT_FALSE(r1.IsEqual(r2));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 50, 50));
+ r1.OrWith(nsRect(0, 60, 50, 50));
+ r1.OrWith(nsRect(100, 60, 50, 50));
+ nsRegion r2(nsRect(0, 0, 50, 50));
+ r2.OrWith(nsRect(0, 60, 50, 50));
+ EXPECT_FALSE(r1.IsEqual(r2));
+ }
+}
+
+TEST(Gfx, RegionOrWith)
+{
+ PR_Sleep(PR_SecondsToInterval(10));
+ {
+ nsRegion r(nsRect(11840, 11840, 4640, -10880));
+ r.OrWith(nsRect(160, 160, 7720, 880));
+ }
+ {
+ nsRegion r(nsRect(79, 31, 75, 12));
+ r.OrWith(nsRect(22, 43, 132, 5));
+ r.OrWith(nsRect(22, 48, 125, 3));
+ r.OrWith(nsRect(22, 51, 96, 20));
+ r.OrWith(nsRect(34, 71, 1, 14));
+ r.OrWith(nsRect(26, 85, 53, 1));
+ r.OrWith(nsRect(26, 86, 53, 4));
+ r.OrWith(nsRect(96, 86, 30, 4));
+ r.OrWith(nsRect(34, 90, 1, 2));
+ r.OrWith(nsRect(96, 90, 30, 2));
+ r.OrWith(nsRect(34, 92, 1, 3));
+ r.OrWith(nsRect(49, 92, 34, 3));
+ r.OrWith(nsRect(96, 92, 30, 3));
+ r.OrWith(nsRect(34, 95, 1, 17));
+ r.OrWith(nsRect(49, 95, 77, 17));
+ r.OrWith(nsRect(34, 112, 1, 12));
+ r.OrWith(nsRect(75, 112, 51, 12));
+ r.OrWith(nsRect(34, 124, 1, 10));
+ r.OrWith(nsRect(75, 124, 44, 10));
+ r.OrWith(nsRect(34, 134, 1, 19));
+ r.OrWith(nsRect(22, 17, 96, 27));
+ }
+ {
+ nsRegion r(nsRect(0, 8, 257, 32));
+ r.OrWith(nsRect(3702, 8, 138, 32));
+ r.OrWith(nsRect(0, 40, 225, 1));
+ r.OrWith(nsRect(3702, 40, 138, 1));
+ r.OrWith(nsRect(0, 41, 101, 40));
+ r.OrWith(nsRect(69, 41, 32, 40));
+ }
+ {
+ nsRegion r(nsRect(79, 56, 8, 32));
+ r.OrWith(nsRect(5, 94, 23, 81));
+ r.OrWith(nsRect(56, 29, 91, 81));
+ }
+ {
+ nsRegion r(nsRect(0, 82, 3840, 2046));
+ r.OrWith(nsRect(0, 0, 3840, 82));
+ }
+ {
+ nsRegion r(nsRect(2, 5, 600, 28));
+ r.OrWith(nsRect(2, 82, 600, 19));
+ r.OrWith(nsRect(2, 33, 600, 49));
+ }
+ {
+ nsRegion r(nsRect(3823, 0, 17, 17));
+ r.OrWith(nsRect(3823, 2029, 17, 17));
+ r.OrWith(nsRect(3823, 0, 17, 2046));
+ }
+ {
+ nsRegion r(nsRect(1036, 4, 32, 21));
+ r.OrWith(nsRect(1070, 4, 66, 21));
+ r.OrWith(nsRect(40, 5, 0, 33));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 1024, 1152));
+ r.OrWith(nsRect(-335802, -1073741824, 1318851, 1860043520));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 800, 1000));
+ r.OrWith(nsRect(0, 0, 536870912, 1073741824));
+ }
+ {
+ nsRegion r(nsRect(53, 2, 52, 3));
+ r.OrWith(nsRect(45, 5, 60, 16));
+ r.OrWith(nsRect(16, 21, 8, 1));
+ r.OrWith(nsRect(45, 21, 12, 1));
+ r.OrWith(nsRect(16, 22, 8, 5));
+ r.OrWith(nsRect(33, 22, 52, 5));
+ r.OrWith(nsRect(16, 27, 8, 7));
+ r.OrWith(nsRect(33, 27, 66, 7));
+ r.OrWith(nsRect(0, 34, 99, 1));
+ r.OrWith(nsRect(0, 35, 159, 27));
+ r.OrWith(nsRect(0, 62, 122, 3));
+ r.OrWith(nsRect(0, 65, 85, 11));
+ r.OrWith(nsRect(91, 65, 97, 11));
+ r.OrWith(nsRect(11, 76, 74, 2));
+ r.OrWith(nsRect(91, 76, 97, 2));
+ r.OrWith(nsRect(11, 78, 74, 12));
+ r.OrWith(nsRect(11, 90, 13, 3));
+ r.OrWith(nsRect(33, 90, 108, 3));
+ r.OrWith(nsRect(16, 93, 8, 22));
+ r.OrWith(nsRect(33, 93, 108, 22));
+ r.OrWith(nsRect(16, 115, 8, 1));
+ r.OrWith(nsRect(58, 115, 83, 1));
+ r.OrWith(nsRect(58, 116, 83, 25));
+ r.OrWith(nsRect(59, 37, 88, 92));
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 100;
+
+ nsRect rects[RectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ }
+#endif
+}
+
+TEST(Gfx, RegionSubWith)
+{
+ {
+ nsRegion r1(nsRect(0, 0, 100, 50));
+ r1.OrWith(nsRect(50, 50, 50, 50));
+ nsRegion r2(nsRect(0, 0, 100, 50));
+ r2.OrWith(nsRect(50, 50, 50, 50));
+ r1.SubWith(r2);
+ EXPECT_FALSE(r1.Contains(1, 1));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 800, 1000));
+ nsRegion r2(nsRect(8, 108, 22, 20));
+ r2.OrWith(nsRect(91, 138, 17, 18));
+ r1.SubWith(r2);
+ EXPECT_TRUE(r1.Contains(400, 130));
+ }
+ {
+ nsRegion r1(nsRect(392, 2, 28, 7));
+ r1.OrWith(nsRect(115, 9, 305, 16));
+ r1.OrWith(nsRect(392, 25, 28, 5));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(0, 0, 1280, 9));
+ r2.OrWith(nsRect(0, 9, 115, 16));
+ r2.OrWith(nsRect(331, 9, 949, 16));
+ r2.OrWith(nsRect(0, 25, 1280, 7));
+ r2.OrWith(nsRect(331, 32, 124, 1));
+ r1.SubWith(r2);
+ EXPECT_FALSE(r1.Contains(350, 15));
+ }
+ {
+ nsRegion r1(nsRect(552, 0, 2, 2));
+ r1.OrWith(nsRect(362, 2, 222, 28));
+ r1.OrWith(nsRect(552, 30, 2, 2));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(512, 0, 146, 9));
+ r2.OrWith(nsRect(340, 9, 318, 16));
+ r2.OrWith(nsRect(512, 25, 146, 8));
+ r1.SubWith(r2);
+ EXPECT_FALSE(r1.Contains(350, 15));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 229380, 6780));
+ r.OrWith(nsRect(76800, 6780, 76800, 4440));
+ r.OrWith(nsRect(76800, 11220, 44082, 1800));
+ r.OrWith(nsRect(122682, 11220, 30918, 1800));
+ r.OrWith(nsRect(76800, 13020, 76800, 2340));
+ r.OrWith(nsRect(85020, 15360, 59340, 75520));
+ r.OrWith(nsRect(85020, 90880, 38622, 11332));
+ r.OrWith(nsRect(143789, 90880, 571, 11332));
+ r.OrWith(nsRect(85020, 102212, 59340, 960));
+ r.OrWith(nsRect(85020, 103172, 38622, 1560));
+ r.OrWith(nsRect(143789, 103172, 571, 1560));
+ r.OrWith(nsRect(85020, 104732, 59340, 12292));
+ r.OrWith(nsRect(85020, 117024, 38622, 1560));
+ r.OrWith(nsRect(143789, 117024, 571, 1560));
+ r.OrWith(nsRect(85020, 118584, 59340, 11976));
+ r.SubWith(nsRect(123642, 89320, 20147, 1560));
+ }
+ {
+ nsRegion r(nsRect(0, 0, 9480, 12900));
+ r.OrWith(nsRect(0, 12900, 8460, 1020));
+ r.SubWith(nsRect(8460, 0, 1020, 12900));
+ }
+ {
+ nsRegion r1(nsRect(99, 1, 51, 2));
+ r1.OrWith(nsRect(85, 3, 65, 1));
+ r1.OrWith(nsRect(10, 4, 66, 5));
+ r1.OrWith(nsRect(85, 4, 37, 5));
+ r1.OrWith(nsRect(10, 9, 112, 3));
+ r1.OrWith(nsRect(1, 12, 121, 1));
+ r1.OrWith(nsRect(1, 13, 139, 3));
+ r1.OrWith(nsRect(0, 16, 140, 3));
+ r1.OrWith(nsRect(0, 19, 146, 3));
+ r1.OrWith(nsRect(0, 22, 149, 2));
+ r1.OrWith(nsRect(0, 24, 154, 2));
+ r1.OrWith(nsRect(0, 26, 160, 23));
+ r1.OrWith(nsRect(0, 49, 162, 31));
+ r1.OrWith(nsRect(0, 80, 171, 19));
+ r1.OrWith(nsRect(0, 99, 173, 11));
+ r1.OrWith(nsRect(2, 110, 171, 6));
+ r1.OrWith(nsRect(6, 116, 165, 5));
+ r1.OrWith(nsRect(8, 121, 163, 1));
+ r1.OrWith(nsRect(13, 122, 158, 11));
+ r1.OrWith(nsRect(14, 133, 157, 23));
+ r1.OrWith(nsRect(29, 156, 142, 10));
+ r1.OrWith(nsRect(37, 166, 134, 6));
+ r1.OrWith(nsRect(55, 172, 4, 4));
+ r1.OrWith(nsRect(83, 172, 88, 4));
+ r1.OrWith(nsRect(55, 176, 4, 2));
+ r1.OrWith(nsRect(89, 176, 6, 2));
+ r1.OrWith(nsRect(89, 178, 6, 4));
+ nsRegion r2(nsRect(63, 11, 39, 11));
+ r2.OrWith(nsRect(63, 22, 99, 16));
+ r2.OrWith(nsRect(37, 38, 125, 61));
+ r2.OrWith(nsRect(45, 99, 117, 8));
+ r2.OrWith(nsRect(47, 107, 115, 7));
+ r2.OrWith(nsRect(47, 114, 66, 1));
+ r2.OrWith(nsRect(49, 115, 64, 2));
+ r2.OrWith(nsRect(49, 117, 54, 30));
+ r1.SubWith(r2);
+ }
+ {
+ nsRegion r1(nsRect(95, 2, 47, 1));
+ r1.OrWith(nsRect(62, 3, 80, 2));
+ r1.OrWith(nsRect(1, 5, 18, 3));
+ r1.OrWith(nsRect(48, 5, 94, 3));
+ r1.OrWith(nsRect(1, 8, 18, 3));
+ r1.OrWith(nsRect(23, 8, 119, 3));
+ r1.OrWith(nsRect(1, 11, 172, 9));
+ r1.OrWith(nsRect(1, 20, 18, 8));
+ r1.OrWith(nsRect(20, 20, 153, 8));
+ r1.OrWith(nsRect(1, 28, 172, 13));
+ r1.OrWith(nsRect(1, 41, 164, 1));
+ r1.OrWith(nsRect(1, 42, 168, 1));
+ r1.OrWith(nsRect(0, 43, 169, 15));
+ r1.OrWith(nsRect(1, 58, 168, 26));
+ r1.OrWith(nsRect(1, 84, 162, 2));
+ r1.OrWith(nsRect(1, 86, 165, 23));
+ r1.OrWith(nsRect(1, 109, 162, 23));
+ r1.OrWith(nsRect(1, 132, 152, 4));
+ r1.OrWith(nsRect(1, 136, 150, 12));
+ r1.OrWith(nsRect(12, 148, 139, 4));
+ r1.OrWith(nsRect(12, 152, 113, 2));
+ r1.OrWith(nsRect(14, 154, 31, 3));
+ r1.OrWith(nsRect(82, 154, 43, 3));
+ r1.OrWith(nsRect(17, 157, 13, 19));
+ r1.OrWith(nsRect(82, 157, 43, 19));
+ r1.OrWith(nsRect(17, 176, 13, 16));
+ nsRegion r2(nsRect(97, 9, 6, 10));
+ r2.OrWith(nsRect(71, 19, 32, 2));
+ r2.OrWith(nsRect(20, 21, 83, 2));
+ r2.OrWith(nsRect(2, 23, 101, 9));
+ r2.OrWith(nsRect(2, 32, 98, 1));
+ r2.OrWith(nsRect(2, 33, 104, 5));
+ r2.OrWith(nsRect(2, 38, 118, 2));
+ r2.OrWith(nsRect(15, 40, 9, 11));
+ r2.OrWith(nsRect(36, 40, 84, 11));
+ r2.OrWith(nsRect(4, 51, 116, 33));
+ r2.OrWith(nsRect(4, 84, 159, 8));
+ r2.OrWith(nsRect(4, 92, 116, 13));
+ r2.OrWith(nsRect(15, 105, 9, 7));
+ r2.OrWith(nsRect(36, 105, 84, 7));
+ r2.OrWith(nsRect(36, 112, 84, 22));
+ r2.OrWith(nsRect(71, 134, 39, 46));
+ r1.SubWith(r2);
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 100;
+ const uint32_t SubRectsPerTest = 10;
+
+ nsRect rects[RectsPerTest];
+ nsRect subRects[SubRectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ r.SubWith(subRects[n]);
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ r2.OrWith(subRects[n]);
+ }
+ r.SubWith(r2);
+
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r(nsRect(-1, -1, 202, 202));
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r.SubWith(subRects[n]);
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ EXPECT_TRUE(r.Contains(-1, -1));
+ EXPECT_TRUE(r.Contains(-1, 200));
+ EXPECT_TRUE(r.Contains(200, -1));
+ EXPECT_TRUE(r.Contains(200, 200));
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r(nsRect(-1, -1, 202, 202));
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r2.OrWith(subRects[n]);
+ }
+ r.SubWith(r2);
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ EXPECT_TRUE(r.Contains(-1, -1));
+ EXPECT_TRUE(r.Contains(-1, 200));
+ EXPECT_TRUE(r.Contains(200, -1));
+ EXPECT_TRUE(r.Contains(200, 200));
+ }
+#endif
+}
+TEST(Gfx, RegionSub)
+{
+ {
+ nsRegion r1(nsRect(0, 0, 100, 50));
+ r1.OrWith(nsRect(50, 50, 50, 50));
+ nsRegion r2(nsRect(0, 0, 100, 50));
+ r2.OrWith(nsRect(50, 50, 50, 50));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_FALSE(r3.Contains(1, 1));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 800, 1000));
+ nsRegion r2(nsRect(8, 108, 22, 20));
+ r2.OrWith(nsRect(91, 138, 17, 18));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_TRUE(r3.Contains(400, 130));
+ }
+ {
+ nsRegion r1(nsRect(392, 2, 28, 7));
+ r1.OrWith(nsRect(115, 9, 305, 16));
+ r1.OrWith(nsRect(392, 25, 28, 5));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(0, 0, 1280, 9));
+ r2.OrWith(nsRect(0, 9, 115, 16));
+ r2.OrWith(nsRect(331, 9, 949, 16));
+ r2.OrWith(nsRect(0, 25, 1280, 7));
+ r2.OrWith(nsRect(331, 32, 124, 1));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_FALSE(r3.Contains(350, 15));
+ }
+ {
+ nsRegion r1(nsRect(552, 0, 2, 2));
+ r1.OrWith(nsRect(362, 2, 222, 28));
+ r1.OrWith(nsRect(552, 30, 2, 2));
+ r1.OrWith(nsRect(0, 32, 1280, 41));
+ nsRegion r2(nsRect(512, 0, 146, 9));
+ r2.OrWith(nsRect(340, 9, 318, 16));
+ r2.OrWith(nsRect(512, 25, 146, 8));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ EXPECT_FALSE(r3.Contains(350, 15));
+ }
+ {
+ nsRegion r1(nsRect(0, 0, 1265, 1024));
+ nsRegion r2(nsRect(1265, 0, 15, 685));
+ r2.OrWith(nsRect(0, 714, 1280, 221));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ }
+ {
+ nsRegion r1(nsRect(6, 0, 64, 1));
+ r1.OrWith(nsRect(6, 1, 67, 1));
+ r1.OrWith(nsRect(6, 2, 67, 2));
+ r1.OrWith(nsRect(79, 2, 67, 2));
+ r1.OrWith(nsRect(6, 4, 67, 1));
+ r1.OrWith(nsRect(79, 4, 98, 1));
+ r1.OrWith(nsRect(6, 5, 171, 18));
+ r1.OrWith(nsRect(1, 23, 176, 3));
+ r1.OrWith(nsRect(1, 26, 178, 5));
+ r1.OrWith(nsRect(1, 31, 176, 9));
+ r1.OrWith(nsRect(0, 40, 177, 57));
+ r1.OrWith(nsRect(0, 97, 176, 33));
+ r1.OrWith(nsRect(0, 130, 12, 17));
+ r1.OrWith(nsRect(15, 130, 161, 17));
+ r1.OrWith(nsRect(0, 147, 12, 5));
+ r1.OrWith(nsRect(15, 147, 111, 5));
+ r1.OrWith(nsRect(0, 152, 12, 7));
+ r1.OrWith(nsRect(17, 152, 109, 7));
+ r1.OrWith(nsRect(0, 159, 12, 2));
+ r1.OrWith(nsRect(17, 159, 98, 2));
+ r1.OrWith(nsRect(17, 161, 98, 9));
+ r1.OrWith(nsRect(27, 170, 63, 21));
+ nsRegion r2(nsRect(9, 9, 37, 17));
+ r2.OrWith(nsRect(92, 9, 26, 17));
+ r2.OrWith(nsRect(9, 26, 37, 9));
+ r2.OrWith(nsRect(84, 26, 65, 9));
+ r2.OrWith(nsRect(9, 35, 37, 2));
+ r2.OrWith(nsRect(51, 35, 98, 2));
+ r2.OrWith(nsRect(51, 37, 98, 11));
+ r2.OrWith(nsRect(51, 48, 78, 4));
+ r2.OrWith(nsRect(87, 52, 42, 7));
+ r2.OrWith(nsRect(19, 59, 12, 5));
+ r2.OrWith(nsRect(87, 59, 42, 5));
+ r2.OrWith(nsRect(19, 64, 12, 9));
+ r2.OrWith(nsRect(32, 64, 97, 9));
+ r2.OrWith(nsRect(19, 73, 12, 2));
+ r2.OrWith(nsRect(32, 73, 104, 2));
+ r2.OrWith(nsRect(19, 75, 117, 5));
+ r2.OrWith(nsRect(18, 80, 118, 5));
+ r2.OrWith(nsRect(18, 85, 111, 38));
+ r2.OrWith(nsRect(87, 123, 42, 11));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ }
+ {
+ nsRegion r1(nsRect(27, 0, 39, 1));
+ r1.OrWith(nsRect(86, 0, 22, 1));
+ r1.OrWith(nsRect(27, 1, 43, 1));
+ r1.OrWith(nsRect(86, 1, 22, 1));
+ r1.OrWith(nsRect(27, 2, 43, 1));
+ r1.OrWith(nsRect(86, 2, 75, 1));
+ r1.OrWith(nsRect(12, 3, 58, 1));
+ r1.OrWith(nsRect(86, 3, 75, 1));
+ r1.OrWith(nsRect(12, 4, 149, 5));
+ r1.OrWith(nsRect(0, 9, 161, 9));
+ r1.OrWith(nsRect(0, 18, 167, 17));
+ r1.OrWith(nsRect(0, 35, 171, 5));
+ r1.OrWith(nsRect(0, 40, 189, 28));
+ r1.OrWith(nsRect(0, 68, 171, 16));
+ r1.OrWith(nsRect(4, 84, 167, 5));
+ r1.OrWith(nsRect(4, 89, 177, 9));
+ r1.OrWith(nsRect(1, 98, 180, 59));
+ r1.OrWith(nsRect(4, 157, 177, 1));
+ r1.OrWith(nsRect(4, 158, 139, 15));
+ r1.OrWith(nsRect(17, 173, 126, 2));
+ r1.OrWith(nsRect(20, 175, 123, 2));
+ r1.OrWith(nsRect(20, 177, 118, 6));
+ r1.OrWith(nsRect(20, 183, 84, 2));
+ nsRegion r2(nsRect(64, 2, 30, 6));
+ r2.OrWith(nsRect(26, 11, 41, 17));
+ r2.OrWith(nsRect(19, 28, 48, 23));
+ r2.OrWith(nsRect(19, 51, 76, 8));
+ r2.OrWith(nsRect(4, 59, 91, 31));
+ r2.OrWith(nsRect(19, 90, 76, 29));
+ r2.OrWith(nsRect(33, 119, 62, 25));
+ r2.OrWith(nsRect(33, 144, 4, 21));
+ nsRegion r3;
+ r3.Sub(r1, r2);
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 100;
+ const uint32_t SubRectsPerTest = 10;
+
+ nsRect rects[RectsPerTest];
+ nsRect subRects[SubRectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ r.SetEmpty();
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rects[n]);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ EXPECT_TRUE(r.Contains(rects[n]));
+ }
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ r2.OrWith(subRects[n]);
+ }
+ nsRegion r3;
+ r3.Sub(r, r2);
+
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ }
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r(nsRect(-1, -1, 202, 202));
+ nsRegion r2;
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ subRects[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r2.OrWith(subRects[n]);
+ }
+ nsRegion r3;
+ r3.Sub(r, r2);
+ for (uint32_t n = 0; n < SubRectsPerTest; n++) {
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].y));
+ EXPECT_FALSE(r3.Contains(subRects[n].x, subRects[n].YMost() - 1));
+ EXPECT_FALSE(
+ r3.Contains(subRects[n].XMost() - 1, subRects[n].YMost() - 1));
+ EXPECT_FALSE(r3.Contains(subRects[n].XMost() - 1, subRects[n].Y()));
+ }
+ EXPECT_TRUE(r3.Contains(-1, -1));
+ EXPECT_TRUE(r3.Contains(-1, 200));
+ EXPECT_TRUE(r3.Contains(200, -1));
+ EXPECT_TRUE(r3.Contains(200, 200));
+ }
+#endif
+}
+
+TEST(Gfx, RegionAndWith)
+{
+ {
+ nsRegion r(nsRect(20, 0, 20, 20));
+ r.OrWith(nsRect(0, 20, 40, 20));
+ r.AndWith(nsRect(0, 0, 5, 5));
+ EXPECT_FALSE(r.Contains(1, 1));
+ }
+ {
+ nsRegion r1(nsRect(512, 1792, 256, 256));
+ nsRegion r2(nsRect(17, 1860, 239, 35));
+ r2.OrWith(nsRect(17, 1895, 239, 7));
+ r2.OrWith(nsRect(768, 1895, 154, 7));
+ r2.OrWith(nsRect(17, 1902, 905, 483));
+ r1.AndWith(r2);
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 50;
+ const uint32_t pointsTested = 100;
+
+ {
+ nsRect rectsSet1[RectsPerTest];
+ nsRect rectsSet2[RectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r1;
+ nsRegion r2;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r1.OrWith(rectsSet1[n]);
+ r2.OrWith(rectsSet1[n]);
+ }
+
+ nsRegion r3 = r1;
+ r3.AndWith(r2);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r3.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r3.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+
+ {
+ nsRect rectsSet[RectsPerTest];
+ nsRect testRect;
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rectsSet[n]);
+ }
+ testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+
+ nsRegion r2 = r;
+ r2.AndWith(testRect);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r2.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r2.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+#endif
+}
+
+TEST(Gfx, RegionAnd)
+{
+ {
+ nsRegion r(nsRect(20, 0, 20, 20));
+ r.OrWith(nsRect(0, 20, 40, 20));
+ nsRegion r2;
+ r2.And(r, nsRect(0, 0, 5, 5));
+ EXPECT_FALSE(r.Contains(1, 1));
+ }
+ {
+ nsRegion r(nsRect(51, 2, 57, 5));
+ r.OrWith(nsRect(36, 7, 72, 4));
+ r.OrWith(nsRect(36, 11, 25, 1));
+ r.OrWith(nsRect(69, 12, 6, 4));
+ r.OrWith(nsRect(37, 16, 54, 2));
+ r.OrWith(nsRect(37, 18, 82, 2));
+ r.OrWith(nsRect(10, 20, 109, 3));
+ r.OrWith(nsRect(1, 23, 136, 21));
+ r.OrWith(nsRect(1, 44, 148, 2));
+ r.OrWith(nsRect(1, 46, 176, 31));
+ r.OrWith(nsRect(6, 77, 171, 1));
+ r.OrWith(nsRect(5, 78, 172, 30));
+ r.OrWith(nsRect(5, 108, 165, 45));
+ r.OrWith(nsRect(5, 153, 61, 5));
+ r.OrWith(nsRect(72, 153, 98, 5));
+ r.OrWith(nsRect(38, 158, 25, 4));
+ r.OrWith(nsRect(72, 158, 98, 4));
+ r.OrWith(nsRect(58, 162, 5, 8));
+ r.OrWith(nsRect(72, 162, 98, 8));
+ r.OrWith(nsRect(72, 170, 98, 5));
+ nsRegion r2;
+ r.And(r2, nsRect(18, 78, 53, 45));
+ }
+#ifdef REGION_RANDOM_STRESS_TESTS
+ const uint32_t TestIterations = 100000;
+ const uint32_t RectsPerTest = 50;
+ const uint32_t pointsTested = 100;
+
+ {
+ nsRect rectsSet1[RectsPerTest];
+ nsRect rectsSet2[RectsPerTest];
+
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r1;
+ nsRegion r2;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet1[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ rectsSet2[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ r1.OrWith(rectsSet1[n]);
+ r2.OrWith(rectsSet1[n]);
+ }
+
+ nsRegion r3;
+ r3.And(r1, r2);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r1.Contains(p.x, p.y) && r2.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r3.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r3.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+
+ {
+ nsRect rectsSet[RectsPerTest];
+ nsRect testRect;
+ for (uint32_t i = 0; i < TestIterations; i++) {
+ nsRegion r;
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ rectsSet[n].SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+ }
+ for (uint32_t n = 0; n < RectsPerTest; n++) {
+ r.OrWith(rectsSet[n]);
+ }
+ testRect.SetRect(rand() % 100, rand() % 100, rand() % 99 + 1,
+ rand() % 99 + 1);
+
+ nsRegion r2;
+ r2.And(r, testRect);
+
+ for (uint32_t n = 0; n < pointsTested; n++) {
+ nsPoint p(rand() % 200, rand() % 200);
+ if (r.Contains(p.x, p.y) && testRect.Contains(p.x, p.y)) {
+ EXPECT_TRUE(r2.Contains(p.x, p.y));
+ } else {
+ EXPECT_FALSE(r2.Contains(p.x, p.y));
+ }
+ }
+ }
+ }
+#endif
+}
+
+TEST(Gfx, RegionSimplify)
+{
+ { // ensure simplify works on a single rect
+ nsRegion r(nsRect(0, 100, 200, 100));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 200, 100));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not the same";
+ }
+
+ { // the rectangles will be merged
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // two rectangle on the first span
+ // one on the second
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 100));
+
+ EXPECT_TRUE(r.GetNumRects() == 3) << "wrong number of rects";
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // the rectangles will be merged
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 100));
+ r.Sub(r, nsRect(200, 200, 40, 200));
+
+ EXPECT_TRUE(r.GetNumRects() == 4) << "wrong number of rects";
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+ result.Sub(result, nsRect(200, 100, 40, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // three spans of rectangles
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 50));
+ r.Sub(r, nsRect(200, 200, 40, 200));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+ result.Sub(result, nsRect(200, 100, 40, 300));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // three spans of rectangles and an unmerged rectangle
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+ r.Or(r, nsRect(250, 100, 50, 50));
+ r.Sub(r, nsRect(200, 200, 40, 200));
+ r.Or(r, nsRect(250, 900, 150, 50));
+
+ r.SimplifyOutwardByArea(100 * 100);
+
+ nsRegion result(nsRect(0, 100, 300, 300));
+ result.Sub(result, nsRect(200, 100, 40, 300));
+ result.Or(result, nsRect(250, 900, 150, 50));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // unmerged regions
+ nsRegion r(nsRect(0, 100, 200, 100));
+ r.Or(r, nsRect(0, 200, 300, 200));
+
+ r.SimplifyOutwardByArea(100);
+
+ nsRegion result(nsRect(0, 100, 200, 100));
+ result.Or(result, nsRect(0, 200, 300, 200));
+
+ EXPECT_TRUE(r.IsEqual(result)) << "regions not merged";
+ }
+
+ { // empty region
+ // just make sure this doesn't crash.
+ nsRegion r;
+ r.SimplifyOutwardByArea(100);
+ }
+}
+
+TEST(Gfx, RegionContains)
+{
+ { // ensure Contains works on a simple region
+ nsRegion r(nsRect(0, 0, 100, 100));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(0, 99));
+ EXPECT_TRUE(r.Contains(99, 0));
+ EXPECT_TRUE(r.Contains(99, 99));
+
+ EXPECT_FALSE(r.Contains(-1, 50));
+ EXPECT_FALSE(r.Contains(100, 50));
+ EXPECT_FALSE(r.Contains(50, -1));
+ EXPECT_FALSE(r.Contains(50, 100));
+
+ EXPECT_TRUE(r.Contains(nsRect(0, 0, 100, 100)));
+ EXPECT_TRUE(r.Contains(nsRect(99, 99, 1, 1)));
+
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 1, 1)));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 0, 0)));
+ }
+
+ { // empty regions contain nothing
+ nsRegion r(nsRect(100, 100, 0, 0));
+
+ EXPECT_FALSE(r.Contains(0, 0));
+ EXPECT_FALSE(r.Contains(100, 100));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 0, 0)));
+ EXPECT_FALSE(r.Contains(nsRect(100, 100, 1, 1)));
+ }
+
+ { // complex region contain tests
+ // The region looks like this, with two squares that overlap.
+ // (hard to do accurately with ASCII art)
+ // +------+
+ // | |
+ // | +--+
+ // | |
+ // +--+ |
+ // | |
+ // +------+
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.OrWith(nsRect(50, 50, 100, 100));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(99, 99));
+ EXPECT_TRUE(r.Contains(50, 100));
+ EXPECT_TRUE(r.Contains(100, 50));
+ EXPECT_TRUE(r.Contains(149, 149));
+
+ EXPECT_FALSE(r.Contains(49, 100));
+ EXPECT_FALSE(r.Contains(100, 49));
+ EXPECT_FALSE(r.Contains(150, 150));
+
+ EXPECT_TRUE(r.Contains(nsRect(100, 100, 1, 1)));
+ EXPECT_FALSE(r.Contains(nsRect(49, 99, 2, 2)));
+ }
+
+ { // region with a hole
+ nsRegion r(nsRect(0, 0, 100, 100));
+ r.SubOut(nsRect(40, 40, 10, 10));
+
+ EXPECT_TRUE(r.Contains(0, 0));
+ EXPECT_TRUE(r.Contains(39, 39));
+ EXPECT_FALSE(r.Contains(40, 40));
+ EXPECT_FALSE(r.Contains(49, 49));
+ EXPECT_TRUE(r.Contains(50, 50));
+
+ EXPECT_FALSE(r.Contains(nsRect(40, 40, 10, 10)));
+ EXPECT_FALSE(r.Contains(nsRect(39, 39, 2, 2)));
+ }
+}
+
+#define DILATE_VALUE 0x88
+#define REGION_VALUE 0xff
+
+struct RegionBitmap {
+ RegionBitmap(unsigned char* bitmap, int width, int height)
+ : bitmap(bitmap), width(width), height(height) {}
+
+ void clear() {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ bitmap[x + y * width] = 0;
+ }
+ }
+ }
+
+ void set(nsRegion& region) {
+ clear();
+ for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) {
+ const nsRect& r = iter.Get();
+ for (int y = r.Y(); y < r.YMost(); y++) {
+ for (int x = r.X(); x < r.XMost(); x++) {
+ bitmap[x + y * width] = REGION_VALUE;
+ }
+ }
+ }
+ }
+
+ void dilate() {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ if (bitmap[x + y * width] == REGION_VALUE) {
+ for (int yn = std::max(y - 1, 0); yn <= std::min(y + 1, height - 1);
+ yn++) {
+ for (int xn = std::max(x - 1, 0); xn <= std::min(x + 1, width - 1);
+ xn++) {
+ if (bitmap[xn + yn * width] == 0)
+ bitmap[xn + yn * width] = DILATE_VALUE;
+ }
+ }
+ }
+ }
+ }
+ }
+ void compare(RegionBitmap& reference) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ EXPECT_EQ(bitmap[x + y * width], reference.bitmap[x + y * width]);
+ }
+ }
+ }
+
+ unsigned char* bitmap;
+ int width;
+ int height;
+};
+
+static void VisitEdge(void* closure, VisitSide side, int x1, int y1, int x2,
+ int y2) {
+ EXPECT_GE(x2, x1);
+ RegionBitmap* visitor = static_cast<RegionBitmap*>(closure);
+ unsigned char* bitmap = visitor->bitmap;
+ const int width = visitor->width;
+
+ if (side == VisitSide::TOP) {
+ while (x1 != x2) {
+ bitmap[x1 + (y1 - 1) * width] = DILATE_VALUE;
+ x1++;
+ }
+ } else if (side == VisitSide::BOTTOM) {
+ while (x1 != x2) {
+ bitmap[x1 + y1 * width] = DILATE_VALUE;
+ x1++;
+ }
+ } else if (side == VisitSide::LEFT) {
+ while (y1 != y2) {
+ bitmap[x1 - 1 + y1 * width] = DILATE_VALUE;
+ y1++;
+ }
+ } else if (side == VisitSide::RIGHT) {
+ while (y1 != y2) {
+ bitmap[x1 + y1 * width] = DILATE_VALUE;
+ y1++;
+ }
+ }
+}
+
+static void TestVisit(nsRegion& r) {
+ auto reference = mozilla::MakeUnique<unsigned char[]>(600 * 600);
+ auto result = mozilla::MakeUnique<unsigned char[]>(600 * 600);
+ RegionBitmap ref(reference.get(), 600, 600);
+ RegionBitmap res(result.get(), 600, 600);
+
+ ref.set(r);
+ ref.dilate();
+
+ res.set(r);
+ r.VisitEdges(VisitEdge, &res);
+ res.compare(ref);
+}
+
+TEST(Gfx, RegionVisitEdges)
+{
+ { // visit edges
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(20, 120, 200, 100));
+ TestVisit(r);
+ }
+
+ { // two rects side by side - 1 pixel inbetween
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(121, 20, 100, 100));
+ TestVisit(r);
+ }
+
+ { // two rects side by side - 2 pixels inbetween
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(122, 20, 100, 100));
+ TestVisit(r);
+ }
+
+ {
+ // only corner of the rects are touching
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 120, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // corners are 1 pixel away
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(121, 120, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // vertically separated
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 125, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // not touching
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(130, 120, 100, 100));
+ r.Or(r, nsRect(240, 20, 100, 100));
+
+ TestVisit(r);
+ }
+
+ { // rect with a hole in it
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Sub(r, nsRect(40, 40, 10, 10));
+
+ TestVisit(r);
+ }
+ {
+ // left overs
+ nsRegion r(nsRect(20, 20, 10, 10));
+ r.Or(r, nsRect(50, 20, 10, 10));
+ r.Or(r, nsRect(90, 20, 10, 10));
+ r.Or(r, nsRect(24, 30, 10, 10));
+ r.Or(r, nsRect(20, 40, 15, 10));
+ r.Or(r, nsRect(50, 40, 15, 10));
+ r.Or(r, nsRect(90, 40, 15, 10));
+
+ TestVisit(r);
+ }
+
+ {
+ // vertically separated
+ nsRegion r(nsRect(20, 20, 100, 100));
+ r.Or(r, nsRect(120, 125, 100, 100));
+
+ TestVisit(r);
+ }
+
+ {
+ // two upper rects followed by a lower one
+ // on the same line
+ nsRegion r(nsRect(5, 5, 50, 50));
+ r.Or(r, nsRect(100, 5, 50, 50));
+ r.Or(r, nsRect(200, 50, 50, 50));
+
+ TestVisit(r);
+ }
+
+ {
+ // bug 1130978.
+ nsRegion r(nsRect(4, 1, 61, 49));
+ r.Or(r, nsRect(115, 1, 99, 49));
+ r.Or(r, nsRect(115, 49, 99, 1));
+ r.Or(r, nsRect(12, 50, 11, 5));
+ r.Or(r, nsRect(25, 50, 28, 5));
+ r.Or(r, nsRect(115, 50, 99, 5));
+ r.Or(r, nsRect(115, 55, 99, 12));
+
+ TestVisit(r);
+ }
+}
+
+// The TiledRegion tests use nsIntRect / IntRegion because nsRect doesn't have
+// InflateToMultiple which is required by TiledRegion.
+TEST(Gfx, TiledRegionNoSimplification2Rects)
+{
+ // Add two rectangles, both rectangles are completely inside
+ // different tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+
+ // No simplification should have happened.
+ EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion()));
+}
+
+TEST(Gfx, TiledRegionNoSimplification1Region)
+{
+ // Add two rectangles, both rectangles are completely inside
+ // different tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(region);
+
+ // No simplification should have happened.
+ EXPECT_TRUE(region.IsEqual(tiledRegion.GetRegion()));
+}
+
+TEST(Gfx, TiledRegionWithSimplification3Rects)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50)));
+}
+
+TEST(Gfx, TiledRegionWithSimplification1Region)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ nsIntRegion region;
+ region.OrWith(nsIntRect(50, 50, 50, 50));
+ region.OrWith(nsIntRect(300, 50, 50, 50));
+ region.OrWith(nsIntRect(250, 70, 10, 10));
+
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(region);
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.GetRegion().IsEqual(nsIntRect(50, 50, 300, 50)));
+}
+
+TEST(Gfx, TiledRegionContains)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 300, 50)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(50, 50, 50, 50)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(50, 50, 301, 50)));
+}
+
+TEST(Gfx, TiledRegionIntersects)
+{
+ // Add three rectangles. The first two rectangles are completely inside
+ // different tiles, but the third rectangle intersects both tiles.
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRect(50, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(300, 50, 50, 50));
+ tiledRegion.Add(nsIntRect(250, 70, 10, 10));
+
+ // Both tiles should have simplified their rectangles, and those two
+ // rectangles are adjacent to each other, so they just build up one rect.
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 300, 50)));
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(200, 10, 10, 50)));
+ EXPECT_TRUE(tiledRegion.Intersects(nsIntRect(50, 50, 301, 50)));
+ EXPECT_FALSE(tiledRegion.Intersects(nsIntRect(0, 0, 50, 500)));
+}
+
+TEST(Gfx, TiledRegionBoundaryConditions1)
+{
+ TiledIntRegion tiledRegion;
+ // This one works fine
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+
+ // This causes the tiledRegion.mBounds to overflow, so it is ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX - 1, INT_MAX - 1, 1, 1)));
+
+ // Verify that the tiledRegion contains only things we expect
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MAX - 1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBoundaryConditions2)
+{
+ TiledIntRegion tiledRegion;
+ // This one works fine
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+
+ // As with TiledRegionBoundaryConditions1, this overflows, so it is ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MAX - 1, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MAX - 1, INT_MIN, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MAX - 1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBigRects)
+{
+ TiledIntRegion tiledRegion;
+ // Super wide region, forces simplification into bounds mode
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN, INT_MAX, 100)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 99, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 100, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-1, INT_MIN + 99, 1, 1)));
+
+ // Add another rect, verify that simplification caused the entire bounds
+ // to expand by a lot more.
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MIN, INT_MIN + 200, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 100, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 200, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-2, INT_MIN + 201, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionBoundaryOverflow)
+{
+ TiledIntRegion tiledRegion;
+ tiledRegion.Add(nsIntRegion(nsIntRect(100, 100, 1, 1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(100, 100, 1, 1)));
+
+ // The next region is invalid, so it gets ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+
+ // Try that again as a rect, it will also get ignored
+ tiledRegion.Add(nsIntRect(INT_MAX, INT_MAX, 1, 1));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 1, 1)));
+
+ // Try with a bigger overflowing rect
+ tiledRegion.Add(nsIntRect(INT_MAX, INT_MAX, 500, 500));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MIN, INT_MIN, 10, 10)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(INT_MAX, INT_MAX, 100, 100)));
+
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+}
+
+TEST(Gfx, TiledRegionNegativeRect)
+{
+ TiledIntRegion tiledRegion;
+ // The next region is invalid, so it gets ignored
+ tiledRegion.Add(nsIntRegion(nsIntRect(0, 0, -500, -500)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-50, -50, 1, 1)));
+ // Rects with negative widths/heights are treated as empty and ignored
+ tiledRegion.Add(nsIntRect(0, 0, -500, -500));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(-1, -1, 1, 1)));
+ EXPECT_FALSE(tiledRegion.Contains(nsIntRect(0, 0, 1, 1)));
+ // Empty rects are always contained
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(0, 0, -1, -1)));
+ EXPECT_TRUE(tiledRegion.Contains(nsIntRect(100, 100, -1, -1)));
+}
diff --git a/gfx/tests/gtest/TestSkipChars.cpp b/gfx/tests/gtest/TestSkipChars.cpp
new file mode 100644
index 0000000000..8f31777619
--- /dev/null
+++ b/gfx/tests/gtest/TestSkipChars.cpp
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "gfxSkipChars.h"
+#include "mozilla/ArrayUtils.h"
+
+static bool TestConstructor() {
+ gfxSkipChars skipChars;
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 0)
+ << "[1] Make sure the gfxSkipChars was properly initialized with "
+ "constructor";
+
+ return true;
+}
+
+static bool TestLength() {
+ gfxSkipChars skipChars;
+
+ skipChars.KeepChars(100);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 100)
+ << "[1] Check length after keeping chars";
+
+ skipChars.SkipChars(50);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 150)
+ << "[2] Check length after skipping chars";
+
+ skipChars.SkipChars(50);
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 200)
+ << "[3] Check length after skipping more chars";
+
+ skipChars.KeepChar();
+
+ EXPECT_TRUE(skipChars.GetOriginalCharCount() == 201)
+ << "[4] Check length after keeping a final char";
+
+ return true;
+}
+
+static bool TestIterator() {
+ // Test a gfxSkipChars that starts with kept chars
+ gfxSkipChars skipChars1;
+
+ skipChars1.KeepChars(9);
+ skipChars1.SkipChar();
+ skipChars1.KeepChars(9);
+ skipChars1.SkipChar();
+ skipChars1.KeepChars(9);
+
+ EXPECT_TRUE(skipChars1.GetOriginalCharCount() == 29) << "[1] Check length";
+
+ gfxSkipCharsIterator iter1(skipChars1);
+
+ EXPECT_TRUE(iter1.GetOriginalOffset() == 0)
+ << "[2] Check initial original offset";
+ EXPECT_TRUE(iter1.GetSkippedOffset() == 0)
+ << "[3] Check initial skipped offset";
+
+ EXPECT_TRUE(iter1.IsOriginalCharSkipped() == false)
+ << "[3a] Check IsOriginalCharSkipped for initial position";
+
+ uint32_t expectSkipped1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 18, 19, 20, 21, 22, 23, 24, 25, 26, 27};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped1); i++) {
+ EXPECT_TRUE(iter1.ConvertOriginalToSkipped(i) == expectSkipped1[i])
+ << "[4] Check mapping of original to skipped for " << i;
+ }
+
+ int32_t expectOriginal1[] = {0, 1, 2, 3, 4, 5, 6, 7, 8,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18,
+ 20, 21, 22, 23, 24, 25, 26, 27, 28};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal1); i++) {
+ EXPECT_TRUE(iter1.ConvertSkippedToOriginal(i) == expectOriginal1[i])
+ << "[5] Check mapping of skipped to original for " << i;
+ }
+
+ bool expectIsOriginalSkipped1[] = {
+ false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, false, false, false, false, false, true,
+ false, false, false, false, false, false, false, false, false};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped1);
+ i++) {
+ iter1.SetOriginalOffset(i);
+ EXPECT_TRUE(iter1.IsOriginalCharSkipped() == expectIsOriginalSkipped1[i])
+ << "[5.a] Check IsOriginalCharSkipped for " << i;
+ }
+
+ // Test a gfxSkipChars that starts with skipped chars
+ gfxSkipChars skipChars2;
+
+ skipChars2.SkipChars(9);
+ skipChars2.KeepChar();
+ skipChars2.SkipChars(9);
+ skipChars2.KeepChar();
+ skipChars2.SkipChars(9);
+
+ EXPECT_TRUE(skipChars2.GetOriginalCharCount() == 29) << "[6] Check length";
+
+ gfxSkipCharsIterator iter2(skipChars2);
+
+ EXPECT_TRUE(iter2.GetOriginalOffset() == 0)
+ << "[7] Check initial original offset";
+ EXPECT_TRUE(iter2.GetSkippedOffset() == 0)
+ << "[8] Check initial skipped offset";
+
+ EXPECT_TRUE(iter2.IsOriginalCharSkipped() == true)
+ << "[8a] Check IsOriginalCharSkipped for initial position";
+
+ uint32_t expectSkipped2[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectSkipped2); i++) {
+ EXPECT_TRUE(iter2.ConvertOriginalToSkipped(i) == expectSkipped2[i])
+ << "[9] Check mapping of original to skipped for " << i;
+ }
+
+ int32_t expectOriginal2[] = {9, 19, 29};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectOriginal2); i++) {
+ EXPECT_TRUE(iter2.ConvertSkippedToOriginal(i) == expectOriginal2[i])
+ << "[10] Check mapping of skipped to original for " << i;
+ }
+
+ bool expectIsOriginalSkipped2[] = {
+ true, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, true, true, false,
+ true, true, true, true, true, true, true, true, true};
+
+ for (uint32_t i = 0; i < mozilla::ArrayLength(expectIsOriginalSkipped2);
+ i++) {
+ iter2.SetOriginalOffset(i);
+ EXPECT_TRUE(iter2.IsOriginalCharSkipped() == expectIsOriginalSkipped2[i])
+ << "[10.a] Check IsOriginalCharSkipped for " << i;
+ }
+
+ return true;
+}
+
+TEST(Gfx, gfxSkipChars)
+{
+ TestConstructor();
+ TestLength();
+ TestIterator();
+}
diff --git a/gfx/tests/gtest/TestSwizzle.cpp b/gfx/tests/gtest/TestSwizzle.cpp
new file mode 100644
index 0000000000..cc91840ebf
--- /dev/null
+++ b/gfx/tests/gtest/TestSwizzle.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/gfx/Swizzle.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+TEST(Moz2D, PremultiplyData)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255,
+ 0, 255, 255, 0, // verify 0 alpha zeroes out RGB
+ 0, 0, 0, 0,
+ 255, 0, 0, 128, // verify that 255 RGB maps to alpha
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+
+ PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::B8G8R8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::R8G8B8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ PremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::A8R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, PremultiplyRow)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255,
+ 0, 255, 255, 0, // verify 0 alpha zeroes out RGB
+ 0, 0, 0, 0,
+ 255, 0, 0, 128, // verify that 255 RGB maps to alpha
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 128,
+ };
+
+ SwizzleRowFn func =
+ PremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ func = PremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ func = PremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::A8R8G8B8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, UnpremultiplyData)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255, 0, 0, 0, 0, // verify 0 alpha leaves RGB at 0
+ 0, 0, 0, 64, // verify 0 RGB stays 0 with non-zero alpha
+ 128, 0, 0, 128, // verify that RGB == alpha maps to 255
+
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 64, 255, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 255, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 128, 0, 0, 255,
+ };
+
+ UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::B8G8R8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::R8G8B8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ UnpremultiplyData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(in_bgra), SurfaceFormat::A8R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, UnpremultiplyRow)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 255, 255, 0, 255, // verify 255 alpha leaves RGB unchanged
+ 0, 0, 255, 255, 0, 0, 0, 0, // verify 0 alpha leaves RGB at 0
+ 0, 0, 0, 64, // verify 0 RGB stays 0 with non-zero alpha
+ 128, 0, 0, 128, // verify that RGB == alpha maps to 255
+
+ };
+ uint8_t out[5 * 4];
+ const uint8_t check_bgra[5 * 4] = {
+ 255, 255, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 0, 0, 0, 64, 255, 0, 0, 128,
+ };
+ // check swizzled output
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 255, 255, 255, 255, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 255, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 128, 0, 0, 255,
+ };
+
+ SwizzleRowFn func =
+ UnpremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ func = UnpremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ func = UnpremultiplyRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::A8R8G8B8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+}
+
+TEST(Moz2D, SwizzleData)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 1, 2, 3, 64, 127, 0, 9, 128,
+
+ };
+ uint8_t out[5 * 4];
+ // check copy
+ const uint8_t check_bgra[5 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 1, 2, 3, 64, 127, 0, 9, 128,
+ };
+ // check swaps
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 0, 3, 2, 1, 64, 9, 0, 127, 128,
+ };
+ const uint8_t check_argb[5 * 4] = {
+ 255, 0, 254, 253, 255, 255, 0, 0, 0, 0, 0, 0, 64, 3, 2, 1, 128, 9, 0, 127,
+ };
+ // check opaquifying
+ const uint8_t check_rgbx[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0,
+ 0, 255, 3, 2, 1, 255, 9, 0, 127, 255,
+ };
+ // check packing
+ uint8_t out24[5 * 3];
+ const uint8_t check_bgr[5 * 3] = {253, 254, 0, 0, 0, 255, 0, 0,
+ 0, 1, 2, 3, 127, 0, 9};
+ const uint8_t check_rgb[5 * 3] = {
+ 0, 254, 253, 255, 0, 0, 0, 0, 0, 3, 2, 1, 9, 0, 127,
+ };
+ uint8_t out8[5];
+ const uint8_t check_a[5] = {255, 255, 0, 64, 128};
+ uint16_t out16[5];
+#define PACK_RGB565(b, g, r) \
+ (((b & 0xF8) >> 3) | ((g & 0xFC) << 3) | ((r & 0xF8) << 8))
+ const uint16_t check_16[5] = {
+ PACK_RGB565(253, 254, 0), PACK_RGB565(0, 0, 255), PACK_RGB565(0, 0, 0),
+ PACK_RGB565(1, 2, 3), PACK_RGB565(127, 0, 9),
+ };
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::B8G8R8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_bgra));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::R8G8B8A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::A8R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_argb));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out,
+ sizeof(out), SurfaceFormat::R8G8B8X8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out, check_rgbx));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out24,
+ sizeof(out24), SurfaceFormat::B8G8R8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out24, check_bgr));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out24,
+ sizeof(out24), SurfaceFormat::R8G8B8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out24, check_rgb));
+
+ SwizzleData(in_bgra, sizeof(in_bgra), SurfaceFormat::B8G8R8A8, out8,
+ sizeof(out8), SurfaceFormat::A8, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out8, check_a));
+
+ SwizzleData(SurfaceFormat::A8R8G8B8_UINT32 == SurfaceFormat::A8R8G8B8
+ ? check_argb
+ : check_bgra,
+ sizeof(in_bgra), SurfaceFormat::A8R8G8B8_UINT32,
+ reinterpret_cast<uint8_t*>(out16), sizeof(out16),
+ SurfaceFormat::R5G6B5_UINT16, IntSize(5, 1));
+ EXPECT_TRUE(ArrayEqual(out16, check_16));
+}
+
+TEST(Moz2D, SwizzleRow)
+{
+ const uint8_t in_bgra[5 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 0, 1, 2, 3, 64, 127, 0, 9, 128,
+
+ };
+ uint8_t out[5 * 4];
+ // check swaps
+ const uint8_t check_rgba[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 0, 3, 2, 1, 64, 9, 0, 127, 128,
+ };
+ // check opaquifying
+ const uint8_t check_rgbx[5 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0,
+ 0, 255, 3, 2, 1, 255, 9, 0, 127, 255,
+ };
+ // check packing
+ uint8_t out24[5 * 3];
+ const uint8_t check_bgr[5 * 3] = {253, 254, 0, 0, 0, 255, 0, 0,
+ 0, 1, 2, 3, 127, 0, 9};
+ const uint8_t check_rgb[5 * 3] = {
+ 0, 254, 253, 255, 0, 0, 0, 0, 0, 3, 2, 1, 9, 0, 127,
+ };
+ // check unpacking
+ uint8_t out_unpack[16 * 4];
+ const uint8_t in_rgb[16 * 3] = {
+ 0, 254, 253, 255, 0, 0, 0, 0, 0, 3, 2, 1, 9, 0, 127, 4,
+ 5, 6, 9, 8, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36,
+ };
+ const uint8_t check_unpack_rgbx[16 * 4] = {
+ 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 255, 3, 2, 1, 255,
+ 9, 0, 127, 255, 4, 5, 6, 255, 9, 8, 7, 255, 10, 11, 12, 255,
+ 13, 14, 15, 255, 16, 17, 18, 255, 19, 20, 21, 255, 22, 23, 24, 255,
+ 25, 26, 27, 255, 28, 29, 30, 255, 31, 32, 33, 255, 34, 35, 36, 255,
+ };
+ const uint8_t check_unpack_bgrx[16 * 4] = {
+ 253, 254, 0, 255, 0, 0, 255, 255, 0, 0, 0, 255, 1, 2, 3, 255,
+ 127, 0, 9, 255, 6, 5, 4, 255, 7, 8, 9, 255, 12, 11, 10, 255,
+ 15, 14, 13, 255, 18, 17, 16, 255, 21, 20, 19, 255, 24, 23, 22, 255,
+ 27, 26, 25, 255, 30, 29, 28, 255, 33, 32, 31, 255, 36, 35, 34, 255,
+ };
+ const uint8_t check_unpack_xrgb[16 * 4] = {
+ 255, 0, 254, 253, 255, 255, 0, 0, 255, 0, 0, 0, 255, 3, 2, 1,
+ 255, 9, 0, 127, 255, 4, 5, 6, 255, 9, 8, 7, 255, 10, 11, 12,
+ 255, 13, 14, 15, 255, 16, 17, 18, 255, 19, 20, 21, 255, 22, 23, 24,
+ 255, 25, 26, 27, 255, 28, 29, 30, 255, 31, 32, 33, 255, 34, 35, 36,
+ };
+
+ SwizzleRowFn func =
+ SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgba));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8X8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, check_rgbx));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8A8);
+ func(in_bgra, out, 5);
+ EXPECT_TRUE(ArrayEqual(out, in_bgra));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::B8G8R8);
+ func(in_bgra, out24, 5);
+ EXPECT_TRUE(ArrayEqual(out24, check_bgr));
+
+ func = SwizzleRow(SurfaceFormat::B8G8R8A8, SurfaceFormat::R8G8B8);
+ func(in_bgra, out24, 5);
+ EXPECT_TRUE(ArrayEqual(out24, check_rgb));
+
+ func = SwizzleRow(SurfaceFormat::R8G8B8, SurfaceFormat::B8G8R8X8);
+ func(in_rgb, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_bgrx));
+
+ memset(out_unpack, 0xE5, sizeof(out_unpack));
+ memcpy(out_unpack, in_rgb, sizeof(in_rgb));
+ func(out_unpack, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_bgrx));
+
+ func = SwizzleRow(SurfaceFormat::R8G8B8, SurfaceFormat::R8G8B8X8);
+ func(in_rgb, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_rgbx));
+
+ memset(out_unpack, 0xE5, sizeof(out_unpack));
+ memcpy(out_unpack, in_rgb, sizeof(in_rgb));
+ func(out_unpack, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_rgbx));
+
+ func = SwizzleRow(SurfaceFormat::R8G8B8, SurfaceFormat::X8R8G8B8);
+ func(in_rgb, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_xrgb));
+
+ memset(out_unpack, 0xE5, sizeof(out_unpack));
+ memcpy(out_unpack, in_rgb, sizeof(in_rgb));
+ func(out_unpack, out_unpack, 16);
+ EXPECT_TRUE(ArrayEqual(out_unpack, check_unpack_xrgb));
+}
diff --git a/gfx/tests/gtest/TestTextureCompatibility.cpp b/gfx/tests/gtest/TestTextureCompatibility.cpp
new file mode 100644
index 0000000000..2e5236614d
--- /dev/null
+++ b/gfx/tests/gtest/TestTextureCompatibility.cpp
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gfxConfig.h"
+#include "gfxPlatform.h"
+#include "gtest/gtest.h"
+#include "MockWidget.h"
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/Compositor.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/RefPtr.h"
+#include "TestLayers.h"
+#include "TextureHelper.h"
+
+using mozilla::gfx::Feature;
+using mozilla::gfx::gfxConfig;
+using mozilla::layers::BasicCompositor;
+using mozilla::layers::Compositor;
+using mozilla::layers::CompositorOptions;
+using mozilla::layers::ISurfaceAllocator;
+using mozilla::layers::LayersBackend;
+using mozilla::layers::TestSurfaceAllocator;
+using mozilla::layers::TextureClient;
+using mozilla::layers::TextureHost;
+using mozilla::widget::CompositorWidget;
+using mozilla::widget::InProcessCompositorWidget;
+
+/**
+ * This function will create the possible TextureClient and TextureHost pairs
+ * according to the given backend.
+ */
+static void CreateTextureWithBackend(
+ LayersBackend& aLayersBackend, ISurfaceAllocator* aDeallocator,
+ nsTArray<RefPtr<TextureClient>>& aTextureClients,
+ nsTArray<RefPtr<TextureHost>>& aTextureHosts) {
+ aTextureClients.AppendElement(CreateTextureClientWithBackend(aLayersBackend));
+
+ aTextureClients.AppendElement(
+ CreateYCbCrTextureClientWithBackend(aLayersBackend));
+
+ for (uint32_t i = 0; i < aTextureClients.Length(); i++) {
+ aTextureHosts.AppendElement(CreateTextureHostWithBackend(
+ aTextureClients[i], aDeallocator, aLayersBackend));
+ }
+}
+
+/**
+ * This will return the default list of backends that units test should run
+ * against.
+ */
+static void GetPlatformBackends(nsTArray<LayersBackend>& aBackends) {
+ gfxPlatform* platform = gfxPlatform::GetPlatform();
+ MOZ_ASSERT(platform);
+
+ platform->GetCompositorBackends(gfxConfig::IsEnabled(Feature::HW_COMPOSITING),
+ aBackends);
+
+ if (aBackends.IsEmpty()) {
+ aBackends.AppendElement(LayersBackend::LAYERS_BASIC);
+ }
+}
+
+/**
+ * This function will return a BasicCompositor to caller.
+ */
+static already_AddRefed<Compositor> CreateBasicCompositor() {
+ RefPtr<Compositor> compositor;
+ // Init the platform.
+ if (gfxPlatform::GetPlatform()) {
+ RefPtr<MockWidget> widget = new MockWidget(256, 256);
+ CompositorOptions options;
+ RefPtr<CompositorWidget> proxy =
+ new InProcessCompositorWidget(options, widget);
+ compositor = new BasicCompositor(nullptr, proxy);
+ }
+ return compositor.forget();
+}
+
+/**
+ * This function checks if the textures react correctly when setting them to
+ * BasicCompositor.
+ */
+static void CheckCompatibilityWithBasicCompositor(
+ LayersBackend aBackends, nsTArray<RefPtr<TextureHost>>& aTextures) {
+ RefPtr<Compositor> compositor = CreateBasicCompositor();
+ for (uint32_t i = 0; i < aTextures.Length(); i++) {
+ if (!aTextures[i]) {
+ continue;
+ }
+ aTextures[i]->SetTextureSourceProvider(compositor);
+
+ // The lock function will fail if the texture is not compatible with
+ // BasicCompositor.
+ bool lockResult = aTextures[i]->Lock();
+ if (aBackends != LayersBackend::LAYERS_BASIC) {
+ EXPECT_FALSE(lockResult);
+ } else {
+ EXPECT_TRUE(lockResult);
+ }
+ if (lockResult) {
+ aTextures[i]->Unlock();
+ }
+ }
+}
+
+TEST(Gfx, TestTextureCompatibility)
+{
+ nsTArray<LayersBackend> backendHints;
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+
+ GetPlatformBackends(backendHints);
+ for (uint32_t i = 0; i < backendHints.Length(); i++) {
+ nsTArray<RefPtr<TextureClient>> textureClients;
+ nsTArray<RefPtr<TextureHost>> textureHosts;
+
+ CreateTextureWithBackend(backendHints[i], deallocator, textureClients,
+ textureHosts);
+ CheckCompatibilityWithBasicCompositor(backendHints[i], textureHosts);
+ }
+}
diff --git a/gfx/tests/gtest/TestTextures.cpp b/gfx/tests/gtest/TestTextures.cpp
new file mode 100644
index 0000000000..4d01ed4c9e
--- /dev/null
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -0,0 +1,307 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gtest/gtest.h"
+#include "gmock/gmock.h"
+#include "TestLayers.h"
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/Tools.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/ImageBridgeChild.h" // for ImageBridgeChild
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#include "mozilla/RefPtr.h"
+#include "gfx2DGlue.h"
+#include "gfxImageSurface.h"
+#include "gfxTypes.h"
+#include "ImageContainer.h"
+#include "mozilla/layers/ImageDataSerializer.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+
+/*
+ * This test performs the following actions:
+ * - creates a surface
+ * - initialize a texture client with it
+ * - serilaizes the texture client
+ * - deserializes the data into a texture host
+ * - reads the surface from the texture host.
+ *
+ * The surface in the end should be equal to the inital one.
+ * This test is run for different combinations of texture types and
+ * image formats.
+ */
+
+namespace mozilla {
+namespace layers {
+
+// fills the surface with values betwee 0 and 100.
+static void SetupSurface(gfxImageSurface* surface) {
+ int bpp = gfxASurface::BytePerPixelFromFormat(surface->Format());
+ int stride = surface->Stride();
+ uint8_t val = 0;
+ uint8_t* data = surface->Data();
+ for (int y = 0; y < surface->Height(); ++y) {
+ for (int x = 0; x < surface->Height(); ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ data[y * stride + x * bpp + b] = val;
+ if (val == 100) {
+ val = 0;
+ } else {
+ ++val;
+ }
+ }
+ }
+ }
+}
+
+// return true if two surfaces contain the same data
+static void AssertSurfacesEqual(gfxImageSurface* surface1,
+ gfxImageSurface* surface2) {
+ ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
+ ASSERT_EQ(surface1->Format(), surface2->Format());
+
+ uint8_t* data1 = surface1->Data();
+ uint8_t* data2 = surface2->Data();
+ int stride1 = surface1->Stride();
+ int stride2 = surface2->Stride();
+ int bpp = gfxASurface::BytePerPixelFromFormat(surface1->Format());
+
+ for (int y = 0; y < surface1->Height(); ++y) {
+ for (int x = 0; x < surface1->Width(); ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ ASSERT_EQ(data1[y * stride1 + x * bpp + b],
+ data2[y * stride2 + x * bpp + b]);
+ }
+ }
+ }
+}
+
+static void AssertSurfacesEqual(SourceSurface* surface1,
+ SourceSurface* surface2) {
+ ASSERT_EQ(surface1->GetSize(), surface2->GetSize());
+ ASSERT_EQ(surface1->GetFormat(), surface2->GetFormat());
+
+ RefPtr<DataSourceSurface> dataSurface1 = surface1->GetDataSurface();
+ RefPtr<DataSourceSurface> dataSurface2 = surface2->GetDataSurface();
+ DataSourceSurface::MappedSurface map1;
+ DataSourceSurface::MappedSurface map2;
+ if (!dataSurface1->Map(DataSourceSurface::READ, &map1)) {
+ return;
+ }
+ if (!dataSurface2->Map(DataSourceSurface::READ, &map2)) {
+ dataSurface1->Unmap();
+ return;
+ }
+ uint8_t* data1 = map1.mData;
+ uint8_t* data2 = map2.mData;
+ int stride1 = map1.mStride;
+ int stride2 = map2.mStride;
+ int bpp = BytesPerPixel(surface1->GetFormat());
+ int width = surface1->GetSize().width;
+ int height = surface1->GetSize().height;
+
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ for (int b = 0; b < bpp; ++b) {
+ ASSERT_EQ(data1[y * stride1 + x * bpp + b],
+ data2[y * stride2 + x * bpp + b]);
+ }
+ }
+ }
+
+ dataSurface1->Unmap();
+ dataSurface2->Unmap();
+}
+
+// Run the test for a texture client and a surface
+void TestTextureClientSurface(TextureClient* texture,
+ gfxImageSurface* surface) {
+ // client allocation
+ ASSERT_TRUE(texture->CanExposeDrawTarget());
+
+ ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
+ // client painting
+ RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
+ RefPtr<SourceSurface> source =
+ gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
+ dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
+
+ RefPtr<SourceSurface> snapshot = dt->Snapshot();
+
+ AssertSurfacesEqual(snapshot, source);
+
+ dt = nullptr; // drop reference before calling Unlock()
+ texture->Unlock();
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ASSERT_TRUE(texture->ToSurfaceDescriptor(descriptor));
+
+ ASSERT_NE(descriptor.type(), SurfaceDescriptor::Tnull_t);
+
+ // host deserialization
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+ RefPtr<TextureHost> host = CreateBackendIndependentTextureHost(
+ descriptor, deallocator, LayersBackend::LAYERS_NONE, texture->GetFlags());
+
+ ASSERT_TRUE(host.get() != nullptr);
+ ASSERT_EQ(host->GetFlags(), texture->GetFlags());
+
+ // host read
+
+ // XXX - this can fail because lock tries to upload the texture but it needs a
+ // Compositor to do that. We could add a DummyComposior for testing but I am
+ // not sure it'll be worth it. Maybe always test against a BasicCompositor,
+ // but the latter needs a widget...
+ if (host->Lock()) {
+ RefPtr<mozilla::gfx::DataSourceSurface> hostDataSurface =
+ host->GetAsSurface();
+
+ DataSourceSurface::ScopedMap map(hostDataSurface, DataSourceSurface::READ);
+ RefPtr<gfxImageSurface> hostSurface = new gfxImageSurface(
+ map.GetData(), hostDataSurface->GetSize(), map.GetStride(),
+ SurfaceFormatToImageFormat(hostDataSurface->GetFormat()));
+ AssertSurfacesEqual(surface, hostSurface.get());
+ host->Unlock();
+ }
+}
+
+// Same as above, for YCbCr surfaces
+void TestTextureClientYCbCr(TextureClient* client, PlanarYCbCrData& ycbcrData) {
+ client->Lock(OpenMode::OPEN_READ_WRITE);
+ UpdateYCbCrTextureClient(client, ycbcrData);
+ client->Unlock();
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ASSERT_TRUE(client->ToSurfaceDescriptor(descriptor));
+
+ ASSERT_EQ(descriptor.type(), SurfaceDescriptor::TSurfaceDescriptorBuffer);
+ auto bufferDesc = descriptor.get_SurfaceDescriptorBuffer();
+ ASSERT_EQ(bufferDesc.desc().type(), BufferDescriptor::TYCbCrDescriptor);
+ auto ycbcrDesc = bufferDesc.desc().get_YCbCrDescriptor();
+ ASSERT_EQ(ycbcrDesc.ySize(), ycbcrData.mYSize);
+ ASSERT_EQ(ycbcrDesc.cbCrSize(), ycbcrData.mCbCrSize);
+ ASSERT_EQ(ycbcrDesc.stereoMode(), ycbcrData.mStereoMode);
+
+ // host deserialization
+ RefPtr<TestSurfaceAllocator> deallocator = new TestSurfaceAllocator();
+ RefPtr<TextureHost> textureHost = CreateBackendIndependentTextureHost(
+ descriptor, deallocator, LayersBackend::LAYERS_NONE, client->GetFlags());
+
+ RefPtr<BufferTextureHost> host =
+ static_cast<BufferTextureHost*>(textureHost.get());
+
+ ASSERT_TRUE(host.get() != nullptr);
+ ASSERT_EQ(host->GetFlags(), client->GetFlags());
+
+ // host read
+
+ if (host->Lock()) {
+ // This will work iff the compositor is not BasicCompositor
+ ASSERT_EQ(host->GetFormat(), mozilla::gfx::SurfaceFormat::YUV);
+ host->Unlock();
+ }
+}
+
+} // namespace layers
+} // namespace mozilla
+
+TEST(Layers, TextureSerialization)
+{
+ // the test is run on all the following image formats
+ gfxImageFormat formats[3] = {
+ SurfaceFormat::A8R8G8B8_UINT32,
+ SurfaceFormat::X8R8G8B8_UINT32,
+ SurfaceFormat::A8,
+ };
+
+ for (int f = 0; f < 3; ++f) {
+ RefPtr<gfxImageSurface> surface =
+ new gfxImageSurface(IntSize(400, 300), formats[f]);
+ SetupSurface(surface.get());
+ AssertSurfacesEqual(surface, surface);
+
+ auto texData = BufferTextureData::Create(
+ surface->GetSize(), gfx::ImageFormatToSurfaceFormat(surface->Format()),
+ gfx::BackendType::CAIRO, LayersBackend::LAYERS_NONE,
+ TextureFlags::DEALLOCATE_CLIENT, ALLOC_DEFAULT, nullptr);
+ ASSERT_TRUE(!!texData);
+
+ RefPtr<TextureClient> client =
+ new TextureClient(texData, TextureFlags::DEALLOCATE_CLIENT, nullptr);
+
+ TestTextureClientSurface(client, surface);
+
+ // XXX - Test more texture client types.
+ }
+}
+
+TEST(Layers, TextureYCbCrSerialization)
+{
+ RefPtr<gfxImageSurface> ySurface =
+ new gfxImageSurface(IntSize(400, 300), SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> cbSurface =
+ new gfxImageSurface(IntSize(200, 150), SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> crSurface =
+ new gfxImageSurface(IntSize(200, 150), SurfaceFormat::A8);
+ SetupSurface(ySurface.get());
+ SetupSurface(cbSurface.get());
+ SetupSurface(crSurface.get());
+
+ PlanarYCbCrData clientData;
+ clientData.mYChannel = ySurface->Data();
+ clientData.mCbChannel = cbSurface->Data();
+ clientData.mCrChannel = crSurface->Data();
+ clientData.mYSize = ySurface->GetSize();
+ clientData.mPicSize = ySurface->GetSize();
+ clientData.mCbCrSize = cbSurface->GetSize();
+ clientData.mYStride = ySurface->Stride();
+ clientData.mCbCrStride = cbSurface->Stride();
+ clientData.mStereoMode = StereoMode::MONO;
+ clientData.mYUVColorSpace = YUVColorSpace::BT601;
+ clientData.mColorDepth = ColorDepth::COLOR_8;
+ clientData.mYSkip = 0;
+ clientData.mCbSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mPicX = 0;
+ clientData.mPicX = 0;
+
+ uint32_t namespaceId = 1;
+ ImageBridgeChild::InitSameProcess(namespaceId);
+
+ RefPtr<ImageBridgeChild> imageBridge = ImageBridgeChild::GetSingleton();
+ static int retry = 5;
+ while (!imageBridge->IPCOpen() && retry) {
+ // IPDL connection takes time especially in slow testing environment, like
+ // VM machines. Here we added retry mechanism to wait for IPDL connnection.
+#ifdef XP_WIN
+ Sleep(1);
+#else
+ sleep(1);
+#endif
+ retry--;
+ }
+
+ // Skip this testing if IPDL connection is not ready
+ if (!retry && !imageBridge->IPCOpen()) {
+ return;
+ }
+
+ RefPtr<TextureClient> client = TextureClient::CreateForYCbCr(
+ imageBridge, clientData.GetPictureRect(), clientData.mYSize,
+ clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride,
+ StereoMode::MONO, ColorDepth::COLOR_8, YUVColorSpace::BT601,
+ ColorRange::LIMITED, TextureFlags::DEALLOCATE_CLIENT);
+
+ TestTextureClientYCbCr(client, clientData);
+
+ // XXX - Test more texture client types.
+}
diff --git a/gfx/tests/gtest/TestTreeTraversal.cpp b/gfx/tests/gtest/TestTreeTraversal.cpp
new file mode 100644
index 0000000000..215baeebf4
--- /dev/null
+++ b/gfx/tests/gtest/TestTreeTraversal.cpp
@@ -0,0 +1,1413 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <vector>
+#include "mozilla/RefPtr.h"
+#include "gtest/gtest.h"
+#include "nsRegion.h"
+#include "nsRect.h"
+#include "TreeTraversal.h"
+#include <stack>
+#include <queue>
+
+const int PERFORMANCE_TREE_DEPTH = 20;
+const int PERFORMANCE_TREE_CHILD_COUNT = 2;
+const int PERFORMANCE_TREE_LEAF_COUNT = 1048576; // 2 ** 20
+const int PERFORMANCE_REGION_XWRAP = 1024;
+
+using namespace mozilla::layers;
+using namespace mozilla;
+
+enum class SearchNodeType { Needle, Hay };
+enum class ForEachNodeType { Continue, Skip };
+
+template <class T>
+class TestNodeBase {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(TestNodeBase<T>);
+ explicit TestNodeBase(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeBase();
+ void SetActualTraversalRank(int aRank);
+ void SetValue(int aValue);
+ void SetType(T aType);
+ void SetRegion(nsRegion aRegion);
+ int GetExpectedTraversalRank();
+ int GetActualTraversalRank();
+ int GetValue();
+ T GetType();
+ nsRegion GetRegion();
+ virtual bool IsLeaf() = 0;
+
+ private:
+ MOZ_INIT_OUTSIDE_CTOR int mExpectedTraversalRank;
+ MOZ_INIT_OUTSIDE_CTOR int mActualTraversalRank;
+ MOZ_INIT_OUTSIDE_CTOR int mValue;
+ MOZ_INIT_OUTSIDE_CTOR nsRegion mRegion;
+ MOZ_INIT_OUTSIDE_CTOR T mType;
+
+ protected:
+ virtual ~TestNodeBase<T>() = default;
+};
+
+template <class T>
+class TestNodeReverse : public TestNodeBase<T> {
+ public:
+ explicit TestNodeReverse(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeReverse();
+ void AddChild(RefPtr<TestNodeReverse<T>> aNode);
+ TestNodeReverse<T>* GetLastChild();
+ TestNodeReverse<T>* GetPrevSibling();
+ bool IsLeaf();
+
+ private:
+ void SetPrevSibling(RefPtr<TestNodeReverse<T>> aNode);
+ void SetLastChild(RefPtr<TestNodeReverse<T>> aNode);
+ RefPtr<TestNodeReverse<T>> mSiblingNode;
+ RefPtr<TestNodeReverse<T>> mLastChildNode;
+ ~TestNodeReverse<T>() = default;
+};
+
+template <class T>
+class TestNodeForward : public TestNodeBase<T> {
+ public:
+ explicit TestNodeForward(T aType, int aExpectedTraversalRank = -1);
+ explicit TestNodeForward();
+ void AddChild(RefPtr<TestNodeForward<T>> aNode);
+ TestNodeForward<T>* GetFirstChild();
+ TestNodeForward<T>* GetNextSibling();
+ bool IsLeaf();
+
+ private:
+ void SetNextSibling(RefPtr<TestNodeForward<T>> aNode);
+ void SetLastChild(RefPtr<TestNodeForward<T>> aNode);
+ void SetFirstChild(RefPtr<TestNodeForward<T>> aNode);
+ RefPtr<TestNodeForward<T>> mSiblingNode = nullptr;
+ RefPtr<TestNodeForward<T>> mFirstChildNode = nullptr;
+ // Track last child to facilitate appending children
+ RefPtr<TestNodeForward<T>> mLastChildNode = nullptr;
+ ~TestNodeForward<T>() = default;
+};
+
+template <class T>
+TestNodeReverse<T>::TestNodeReverse(T aType, int aExpectedTraversalRank)
+ : TestNodeBase<T>(aType, aExpectedTraversalRank) {}
+
+template <class T>
+TestNodeReverse<T>::TestNodeReverse() : TestNodeBase<T>() {}
+
+template <class T>
+void TestNodeReverse<T>::SetLastChild(RefPtr<TestNodeReverse<T>> aNode) {
+ mLastChildNode = aNode;
+}
+
+template <class T>
+void TestNodeReverse<T>::AddChild(RefPtr<TestNodeReverse<T>> aNode) {
+ aNode->SetPrevSibling(mLastChildNode);
+ SetLastChild(aNode);
+}
+
+template <class T>
+void TestNodeReverse<T>::SetPrevSibling(RefPtr<TestNodeReverse<T>> aNode) {
+ mSiblingNode = aNode;
+}
+
+template <class T>
+TestNodeReverse<T>* TestNodeReverse<T>::GetLastChild() {
+ return mLastChildNode;
+}
+
+template <class T>
+TestNodeReverse<T>* TestNodeReverse<T>::GetPrevSibling() {
+ return mSiblingNode;
+}
+
+template <class T>
+bool TestNodeReverse<T>::IsLeaf() {
+ return !mLastChildNode;
+}
+
+template <class T>
+TestNodeForward<T>::TestNodeForward(T aType, int aExpectedTraversalRank)
+ : TestNodeBase<T>(aType, aExpectedTraversalRank) {}
+
+template <class T>
+TestNodeForward<T>::TestNodeForward() : TestNodeBase<T>() {}
+
+template <class T>
+void TestNodeForward<T>::AddChild(RefPtr<TestNodeForward<T>> aNode) {
+ if (mFirstChildNode == nullptr) {
+ SetFirstChild(aNode);
+ SetLastChild(aNode);
+ } else {
+ mLastChildNode->SetNextSibling(aNode);
+ SetLastChild(aNode);
+ }
+}
+
+template <class T>
+void TestNodeForward<T>::SetLastChild(RefPtr<TestNodeForward<T>> aNode) {
+ mLastChildNode = aNode;
+}
+
+template <class T>
+void TestNodeForward<T>::SetFirstChild(RefPtr<TestNodeForward<T>> aNode) {
+ mFirstChildNode = aNode;
+}
+
+template <class T>
+void TestNodeForward<T>::SetNextSibling(RefPtr<TestNodeForward<T>> aNode) {
+ mSiblingNode = aNode;
+}
+
+template <class T>
+bool TestNodeForward<T>::IsLeaf() {
+ return !mFirstChildNode;
+}
+
+template <class T>
+TestNodeForward<T>* TestNodeForward<T>::GetFirstChild() {
+ return mFirstChildNode;
+}
+
+template <class T>
+TestNodeForward<T>* TestNodeForward<T>::GetNextSibling() {
+ return mSiblingNode;
+}
+
+template <class T>
+TestNodeBase<T>::TestNodeBase(T aType, int aExpectedTraversalRank)
+ : mExpectedTraversalRank(aExpectedTraversalRank),
+ mActualTraversalRank(-1),
+ mType(aType) {}
+
+template <class T>
+TestNodeBase<T>::TestNodeBase() = default;
+
+template <class T>
+int TestNodeBase<T>::GetActualTraversalRank() {
+ return mActualTraversalRank;
+}
+
+template <class T>
+void TestNodeBase<T>::SetActualTraversalRank(int aRank) {
+ mActualTraversalRank = aRank;
+}
+
+template <class T>
+int TestNodeBase<T>::GetExpectedTraversalRank() {
+ return mExpectedTraversalRank;
+}
+
+template <class T>
+T TestNodeBase<T>::GetType() {
+ return mType;
+}
+
+template <class T>
+void TestNodeBase<T>::SetType(T aType) {
+ mType = aType;
+}
+
+template <class T>
+nsRegion TestNodeBase<T>::GetRegion() {
+ return mRegion;
+}
+
+template <class T>
+void TestNodeBase<T>::SetRegion(nsRegion aRegion) {
+ mRegion = aRegion;
+}
+
+template <class T>
+int TestNodeBase<T>::GetValue() {
+ return mValue;
+}
+
+template <class T>
+void TestNodeBase<T>::SetValue(int aValue) {
+ mValue = aValue;
+}
+
+typedef TestNodeBase<SearchNodeType> SearchTestNode;
+typedef TestNodeBase<ForEachNodeType> ForEachTestNode;
+typedef TestNodeReverse<SearchNodeType> SearchTestNodeReverse;
+typedef TestNodeReverse<ForEachNodeType> ForEachTestNodeReverse;
+typedef TestNodeForward<SearchNodeType> SearchTestNodeForward;
+typedef TestNodeForward<ForEachNodeType> ForEachTestNodeForward;
+
+TEST(TreeTraversal, DepthFirstSearchNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearch<layers::ReverseIterator>(
+ nullNode.get(), [](SearchTestNodeReverse* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr)
+ << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root =
+ new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ nullNode.get(), [](SearchTestNodeReverse* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr)
+ << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[3]);
+ nodeList[5]->AddChild(nodeList[4]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchPostOrder<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[4]);
+ nodeList[5]->AddChild(nodeList[3]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root =
+ new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[3]);
+ nodeList[5]->AddChild(nodeList[4]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ DepthFirstSearchPostOrder<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[9];
+ nodeList[9]->AddChild(nodeList[8]);
+ nodeList[9]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[0]);
+ nodeList[8]->AddChild(nodeList[7]);
+ nodeList[8]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[5]);
+ nodeList[5]->AddChild(nodeList[4]);
+ nodeList[5]->AddChild(nodeList[3]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ DepthFirstSearchPostOrder<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (int i = 0; i < 10; i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchNull)
+{
+ RefPtr<SearchTestNodeReverse> nullNode;
+ RefPtr<SearchTestNodeReverse> result =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ nullNode.get(), [](SearchTestNodeReverse* aNode) {
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result.get(), nullptr)
+ << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchRootIsNeedle)
+{
+ RefPtr<SearchTestNodeReverse> root =
+ new SearchTestNodeReverse(SearchNodeType::Needle, 0);
+ RefPtr<SearchTestNodeReverse> childNode1 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ RefPtr<SearchTestNodeReverse> childNode2 =
+ new SearchTestNodeReverse(SearchNodeType::Hay);
+ int visitCount = 0;
+ RefPtr<SearchTestNodeReverse> result =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+ ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Search starting at needle did not return needle.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+ ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+ childNode2->GetActualTraversalRank())
+ << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueExists)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeForward> needleNode;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeForward(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ BreadthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueExistsReverse)
+{
+ int visitCount = 0;
+ size_t expectedNeedleTraversalRank = 7;
+ RefPtr<SearchTestNodeReverse> needleNode;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (size_t i = 0; i < 10; i++) {
+ if (i == expectedNeedleTraversalRank) {
+ needleNode = new SearchTestNodeReverse(SearchNodeType::Needle, i);
+ nodeList.push_back(needleNode);
+ } else if (i < expectedNeedleTraversalRank) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ } else {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay));
+ }
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+ ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+ << "Returned node does not match expected value (something odd "
+ "happened).";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExist)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeForward(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ RefPtr<SearchTestNodeForward> foundNode =
+ BreadthFirstSearch<layers::ForwardIterator>(
+ root.get(), [&visitCount](SearchTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, BreadthFirstSearchValueDoesNotExistReverse)
+{
+ int visitCount = 0;
+ std::vector<RefPtr<SearchTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(new SearchTestNodeReverse(SearchNodeType::Hay, i));
+ }
+
+ RefPtr<SearchTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[6]);
+ nodeList[2]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ RefPtr<SearchTestNodeReverse> foundNode =
+ BreadthFirstSearch<layers::ReverseIterator>(
+ root.get(), [&visitCount](SearchTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == SearchNodeType::Needle;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ ASSERT_EQ(foundNode.get(), nullptr)
+ << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, ForEachNodeNullStillRuns)
+{
+ RefPtr<ForEachTestNodeReverse> nullNode;
+ ForEachNode<layers::ReverseIterator>(
+ nullNode.get(),
+ [](ForEachTestNodeReverse* aNode) { return TraversalFlag::Continue; });
+}
+
+TEST(TreeTraversal, ForEachNodeAllEligible)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> nodeList;
+ int visitCount = 0;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, i));
+ }
+
+ RefPtr<ForEachTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ ForEachNode<layers::ForwardIterator>(
+ root.get(), [&visitCount](ForEachTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeAllEligibleReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ int visitCount = 0;
+ nodeList.reserve(10);
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeSomeIneligibleNodes)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> expectedVisitedNodeList;
+ std::vector<RefPtr<ForEachTestNodeForward>> expectedSkippedNodeList;
+ int visitCount = 0;
+
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, 0));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip, 1));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, 2));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip, 3));
+
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Skip));
+
+ RefPtr<ForEachTestNodeForward> root = expectedVisitedNodeList[0];
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+ expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
+
+ ForEachNode<layers::ForwardIterator>(
+ root.get(), [&visitCount](ForEachTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) {
+ ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(),
+ expectedVisitedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ for (size_t i = 0; i < expectedSkippedNodeList.size(); i++) {
+ ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(),
+ expectedSkippedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << "was not expected to be hit.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeSomeIneligibleNodesReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> expectedVisitedNodeList;
+ std::vector<RefPtr<ForEachTestNodeReverse>> expectedSkippedNodeList;
+ int visitCount = 0;
+
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, 0));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip, 1));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, 2));
+ expectedVisitedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip, 3));
+
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip));
+ expectedSkippedNodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Skip));
+
+ RefPtr<ForEachTestNodeReverse> root = expectedVisitedNodeList[0];
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
+ expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+ expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
+ expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
+ expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < expectedVisitedNodeList.size(); i++) {
+ ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(),
+ expectedVisitedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+
+ for (size_t i = 0; i < expectedSkippedNodeList.size(); i++) {
+ ASSERT_EQ(expectedSkippedNodeList[i]->GetExpectedTraversalRank(),
+ expectedSkippedNodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << "was not expected to be hit.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeIneligibleRoot)
+{
+ int visitCount = 0;
+
+ RefPtr<ForEachTestNodeReverse> root =
+ new ForEachTestNodeReverse(ForEachNodeType::Skip, 0);
+ RefPtr<ForEachTestNodeReverse> childNode1 =
+ new ForEachTestNodeReverse(ForEachNodeType::Continue);
+ RefPtr<ForEachTestNodeReverse> chlidNode2 =
+ new ForEachTestNodeReverse(ForEachNodeType::Skip);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+ << "Root was hit out of order.";
+ ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+ childNode1->GetActualTraversalRank())
+ << "Eligible child was still hit.";
+ ASSERT_EQ(chlidNode2->GetExpectedTraversalRank(),
+ chlidNode2->GetActualTraversalRank())
+ << "Ineligible child was still hit.";
+}
+
+TEST(TreeTraversal, ForEachNodeLeavesIneligible)
+{
+ std::vector<RefPtr<ForEachTestNodeForward>> nodeList;
+ nodeList.reserve(10);
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++) {
+ if (i == 1 || i == 9) {
+ nodeList.push_back(new ForEachTestNodeForward(ForEachNodeType::Skip, i));
+ } else {
+ nodeList.push_back(
+ new ForEachTestNodeForward(ForEachNodeType::Continue, i));
+ }
+ }
+
+ RefPtr<ForEachTestNodeForward> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[2]->AddChild(nodeList[3]);
+ nodeList[2]->AddChild(nodeList[4]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[8]);
+ nodeList[7]->AddChild(nodeList[9]);
+
+ ForEachNode<layers::ForwardIterator>(
+ root.get(), [&visitCount](ForEachTestNodeForward* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeLeavesIneligibleReverse)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++) {
+ if (i == 1 || i == 9) {
+ nodeList.push_back(new ForEachTestNodeReverse(ForEachNodeType::Skip, i));
+ } else {
+ nodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[2]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[2]->AddChild(nodeList[4]);
+ nodeList[2]->AddChild(nodeList[3]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ return aNode->GetType() == ForEachNodeType::Continue
+ ? TraversalFlag::Continue
+ : TraversalFlag::Skip;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+TEST(TreeTraversal, ForEachNodeLambdaReturnsVoid)
+{
+ std::vector<RefPtr<ForEachTestNodeReverse>> nodeList;
+ nodeList.reserve(10);
+ int visitCount = 0;
+ for (int i = 0; i < 10; i++) {
+ nodeList.push_back(
+ new ForEachTestNodeReverse(ForEachNodeType::Continue, i));
+ }
+
+ RefPtr<ForEachTestNodeReverse> root = nodeList[0];
+ nodeList[0]->AddChild(nodeList[4]);
+ nodeList[0]->AddChild(nodeList[1]);
+ nodeList[1]->AddChild(nodeList[3]);
+ nodeList[1]->AddChild(nodeList[2]);
+ nodeList[4]->AddChild(nodeList[6]);
+ nodeList[4]->AddChild(nodeList[5]);
+ nodeList[6]->AddChild(nodeList[7]);
+ nodeList[7]->AddChild(nodeList[9]);
+ nodeList[7]->AddChild(nodeList[8]);
+
+ ForEachNode<layers::ReverseIterator>(
+ root.get(), [&visitCount](ForEachTestNodeReverse* aNode) {
+ aNode->SetActualTraversalRank(visitCount);
+ visitCount++;
+ });
+
+ for (size_t i = 0; i < nodeList.size(); i++) {
+ ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+ nodeList[i]->GetActualTraversalRank())
+ << "Node at index " << i << " was hit out of order.";
+ }
+}
+
+struct AssignSearchNodeTypesWithLastLeafAsNeedle {
+ RefPtr<SearchTestNodeForward>& node;
+ void operator()(SearchTestNodeForward* aNode) {
+ aNode->SetType(SearchNodeType::Hay);
+ if (aNode->IsLeaf()) {
+ node = aNode;
+ }
+ }
+};
+
+struct AssignSearchNodeTypesAllHay {
+ void operator()(SearchTestNode* aNode) {
+ aNode->SetType(SearchNodeType::Hay);
+ }
+};
+
+struct AssignSearchNodeTypesWithFirstLeafAsNeedle {
+ RefPtr<SearchTestNodeReverse>& needleNode;
+ void operator()(SearchTestNodeReverse* aNode) {
+ if (!needleNode && aNode->IsLeaf()) {
+ needleNode = aNode;
+ }
+ aNode->SetType(SearchNodeType::Hay);
+ }
+};
+
+struct AssignSearchNodeValuesAllFalseValuesReverse {
+ int falseValue;
+ RefPtr<SearchTestNodeReverse>& needleNode;
+ void operator()(SearchTestNodeReverse* aNode) {
+ aNode->SetValue(falseValue);
+ if (!needleNode && aNode->IsLeaf()) {
+ needleNode = aNode;
+ }
+ }
+};
+
+struct AssignSearchNodeValuesAllFalseValuesForward {
+ int falseValue;
+ RefPtr<SearchTestNodeForward>& needleNode;
+ void operator()(SearchTestNodeForward* aNode) {
+ aNode->SetValue(falseValue);
+ needleNode = aNode;
+ }
+};
+
+struct AllocateUnitRegionsToLeavesOnly {
+ int& xWrap;
+ int& squareCount;
+ void operator()(ForEachTestNode* aNode) {
+ if (aNode->IsLeaf()) {
+ int x = squareCount % xWrap;
+ int y = squareCount / xWrap;
+ aNode->SetRegion(nsRegion(nsRect(x, y, 1, 1)));
+ squareCount++;
+ }
+ }
+};
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchForwardRecursive(RefPtr<Node> aNode) {
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchForwardRecursive(node)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchCaptureVariablesForwardRecursive(
+ RefPtr<Node> aNode, int a, int b, int c, int d, int e, int f, int g, int h,
+ int i, int j, int k, int l, int m, int& n, int& o, int& p, int& q, int& r,
+ int& s, int& t, int& u, int& v, int& w, int& x, int& y, int& z) {
+ if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y +
+ z) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchCaptureVariablesForwardRecursive(
+ node, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s,
+ t, u, v, w, x, y, z)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchPostOrderForwardRecursive(
+ RefPtr<Node> aNode) {
+ for (RefPtr<Node> node = aNode->GetFirstChild(); node != nullptr;
+ node = node->GetNextSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchPostOrderForwardRecursive(node)) {
+ return foundNode;
+ }
+ }
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> BreadthFirstSearchForwardQueue(RefPtr<Node> aNode) {
+ std::queue<RefPtr<Node>> nodes;
+ nodes.push(aNode);
+ while (!nodes.empty()) {
+ RefPtr<Node> node = nodes.front();
+ nodes.pop();
+ if (node->GetType() == SearchNodeType::Needle) {
+ return node;
+ }
+ for (RefPtr<Node> childNode = node->GetFirstChild(); childNode != nullptr;
+ childNode = childNode->GetNextSibling()) {
+ nodes.push(childNode);
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchReverseRecursive(RefPtr<Node> aNode) {
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetLastChild(); node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode = DepthFirstSearchReverseRecursive(node)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchCaptureVariablesReverseRecursive(
+ RefPtr<Node> aNode, int a, int b, int c, int d, int e, int f, int g, int h,
+ int i, int j, int k, int l, int m, int& n, int& o, int& p, int& q, int& r,
+ int& s, int& t, int& u, int& v, int& w, int& x, int& y, int& z) {
+ if (aNode->GetValue() == a + b + c + d + e + f + g + h + i + j + k + l + m +
+ n + o + p + q + r + s + t + u + v + w + x + y +
+ z) {
+ return aNode;
+ }
+ for (RefPtr<Node> node = aNode->GetLastChild(); node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchCaptureVariablesReverseRecursive(
+ node, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s,
+ t, u, v, w, x, y, z)) {
+ return foundNode;
+ }
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> DepthFirstSearchPostOrderReverseRecursive(
+ RefPtr<Node> aNode) {
+ for (RefPtr<Node> node = aNode->GetLastChild(); node != nullptr;
+ node = node->GetPrevSibling()) {
+ if (RefPtr<Node> foundNode =
+ DepthFirstSearchPostOrderReverseRecursive(node)) {
+ return foundNode;
+ }
+ }
+ if (aNode->GetType() == SearchNodeType::Needle) {
+ return aNode;
+ }
+ return nullptr;
+}
+
+template <typename Node>
+static RefPtr<Node> BreadthFirstSearchReverseQueue(RefPtr<Node> aNode) {
+ std::queue<RefPtr<Node>> nodes;
+ nodes.push(aNode);
+ while (!nodes.empty()) {
+ RefPtr<Node> node = nodes.front();
+ nodes.pop();
+ if (node->GetType() == SearchNodeType::Needle) {
+ return node;
+ }
+ for (RefPtr<Node> childNode = node->GetLastChild(); childNode != nullptr;
+ childNode = childNode->GetPrevSibling()) {
+ nodes.push(childNode);
+ }
+ }
+ return nullptr;
+}
diff --git a/gfx/tests/gtest/TestVsync.cpp b/gfx/tests/gtest/TestVsync.cpp
new file mode 100644
index 0000000000..ed77c59b08
--- /dev/null
+++ b/gfx/tests/gtest/TestVsync.cpp
@@ -0,0 +1,203 @@
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "gfxPlatform.h"
+
+#include "MainThreadUtils.h"
+#include "nsIThread.h"
+#include "mozilla/RefPtr.h"
+#include "SoftwareVsyncSource.h"
+#include "VsyncSource.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/VsyncDispatcher.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+using namespace mozilla::layers;
+using ::testing::_;
+
+// Timeout for vsync events to occur in milliseconds
+// Windows 8.1 has intermittents at 50 ms. Raise limit to 5 vsync intervals.
+const int kVsyncTimeoutMS = 80;
+
+class TestVsyncObserver : public VsyncObserver {
+ public:
+ TestVsyncObserver()
+ : mDidGetVsyncNotification(false), mVsyncMonitor("VsyncMonitor") {}
+
+ virtual bool NotifyVsync(const VsyncEvent& aVsync) override {
+ MonitorAutoLock lock(mVsyncMonitor);
+ mDidGetVsyncNotification = true;
+ mVsyncMonitor.Notify();
+ return true;
+ }
+
+ void WaitForVsyncNotification() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (DidGetVsyncNotification()) {
+ return;
+ }
+
+ { // scope lock
+ MonitorAutoLock lock(mVsyncMonitor);
+ lock.Wait(TimeDuration::FromMilliseconds(kVsyncTimeoutMS));
+ }
+ }
+
+ bool DidGetVsyncNotification() {
+ MonitorAutoLock lock(mVsyncMonitor);
+ return mDidGetVsyncNotification;
+ }
+
+ void ResetVsyncNotification() {
+ MonitorAutoLock lock(mVsyncMonitor);
+ mDidGetVsyncNotification = false;
+ }
+
+ private:
+ bool mDidGetVsyncNotification;
+
+ private:
+ Monitor mVsyncMonitor;
+};
+
+class VsyncTester : public ::testing::Test {
+ protected:
+ explicit VsyncTester() {
+ gfxPlatform::GetPlatform();
+ mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
+ MOZ_RELEASE_ASSERT(mVsyncSource, "GFX: Vsync source not found.");
+ }
+
+ virtual ~VsyncTester() { mVsyncSource = nullptr; }
+
+ RefPtr<VsyncSource> mVsyncSource;
+};
+
+static void FlushMainThreadLoop() {
+ // Some tasks are pushed onto the main thread when adding vsync observers
+ // This function will ensure all tasks are executed on the main thread
+ // before returning.
+ nsCOMPtr<nsIThread> mainThread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(mainThread));
+ ASSERT_TRUE(NS_SUCCEEDED(rv));
+
+ rv = NS_OK;
+ bool processed = true;
+ while (processed && NS_SUCCEEDED(rv)) {
+ rv = mainThread->ProcessNextEvent(false, &processed);
+ }
+}
+
+// Tests that we can enable/disable vsync notifications
+TEST_F(VsyncTester, EnableVsync) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ globalDisplay.EnableVsync();
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that if we have vsync enabled, the display should get vsync
+// notifications
+TEST_F(VsyncTester, CompositorGetVsyncNotifications) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<CompositorVsyncDispatcher> vsyncDispatcher =
+ new CompositorVsyncDispatcher();
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+
+ vsyncDispatcher->SetCompositorVsyncObserver(testVsyncObserver);
+ FlushMainThreadLoop();
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that if we have vsync enabled, the parent refresh driver should get
+// notifications
+TEST_F(VsyncTester, ParentRefreshDriverGetVsyncNotifications) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
+ globalDisplay.GetRefreshTimerVsyncDispatcher();
+ ASSERT_TRUE(vsyncDispatcher != nullptr);
+
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+ vsyncDispatcher->SetParentRefreshTimer(testVsyncObserver);
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+ vsyncDispatcher->SetParentRefreshTimer(nullptr);
+
+ testVsyncObserver->ResetVsyncNotification();
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that child refresh vsync observers get vsync notifications
+TEST_F(VsyncTester, ChildRefreshDriverGetVsyncNotifications) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+
+ RefPtr<RefreshTimerVsyncDispatcher> vsyncDispatcher =
+ globalDisplay.GetRefreshTimerVsyncDispatcher();
+ ASSERT_TRUE(vsyncDispatcher != nullptr);
+
+ RefPtr<TestVsyncObserver> testVsyncObserver = new TestVsyncObserver();
+ vsyncDispatcher->AddChildRefreshTimer(testVsyncObserver);
+ ASSERT_TRUE(globalDisplay.IsVsyncEnabled());
+
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_TRUE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher->RemoveChildRefreshTimer(testVsyncObserver);
+ testVsyncObserver->ResetVsyncNotification();
+ testVsyncObserver->WaitForVsyncNotification();
+ ASSERT_FALSE(testVsyncObserver->DidGetVsyncNotification());
+
+ vsyncDispatcher = nullptr;
+ testVsyncObserver = nullptr;
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
+
+// Test that we can read the vsync rate
+TEST_F(VsyncTester, VsyncSourceHasVsyncRate) {
+ VsyncSource::Display& globalDisplay = mVsyncSource->GetGlobalDisplay();
+ TimeDuration vsyncRate = globalDisplay.GetVsyncRate();
+ ASSERT_NE(vsyncRate, TimeDuration::Forever());
+ ASSERT_GT(vsyncRate.ToMilliseconds(), 0);
+
+ globalDisplay.DisableVsync();
+ ASSERT_FALSE(globalDisplay.IsVsyncEnabled());
+}
diff --git a/gfx/tests/gtest/TextureHelper.h b/gfx/tests/gtest/TextureHelper.h
new file mode 100644
index 0000000000..db0817b3ad
--- /dev/null
+++ b/gfx/tests/gtest/TextureHelper.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <vector>
+
+#include "Types.h"
+#include "gfxImageSurface.h"
+#include "gfxPlatform.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/gfx/Point.h"
+#include "mozilla/layers/BufferTexture.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/layers/TextureClient.h"
+#include "mozilla/layers/TextureHost.h"
+#ifdef XP_WIN
+# include "IMFYCbCrImage.h"
+# include "mozilla/gfx/DeviceManagerDx.h"
+# include "mozilla/layers/D3D11YCbCrImage.h"
+# include "mozilla/layers/TextureD3D11.h"
+# include "mozilla/layers/TextureDIB.h"
+#endif
+
+namespace mozilla {
+namespace layers {
+
+using gfx::BackendType;
+using gfx::IntSize;
+using gfx::SurfaceFormat;
+
+/**
+ * Create a YCbCrTextureClient according to the given backend.
+ */
+static already_AddRefed<TextureClient> CreateYCbCrTextureClientWithBackend(
+ LayersBackend aLayersBackend) {
+ TextureData* data = nullptr;
+ IntSize size = IntSize(200, 150);
+ IntSize ySize = IntSize(400, 300);
+
+ RefPtr<gfxImageSurface> ySurface =
+ new gfxImageSurface(ySize, SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> cbSurface =
+ new gfxImageSurface(size, SurfaceFormat::A8);
+ RefPtr<gfxImageSurface> crSurface =
+ new gfxImageSurface(size, SurfaceFormat::A8);
+
+ PlanarYCbCrData clientData;
+ clientData.mYChannel = ySurface->Data();
+ clientData.mCbChannel = cbSurface->Data();
+ clientData.mCrChannel = crSurface->Data();
+ clientData.mYSize = ySurface->GetSize();
+ clientData.mPicSize = ySurface->GetSize();
+ clientData.mCbCrSize = cbSurface->GetSize();
+ clientData.mYStride = ySurface->Stride();
+ clientData.mCbCrStride = cbSurface->Stride();
+ clientData.mStereoMode = StereoMode::MONO;
+ clientData.mYSkip = 0;
+ clientData.mCbSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mCrSkip = 0;
+ clientData.mPicX = 0;
+ clientData.mPicX = 0;
+
+ // Create YCbCrTexture for basic backend.
+ if (aLayersBackend == LayersBackend::LAYERS_BASIC) {
+ return TextureClient::CreateForYCbCr(
+ nullptr, clientData.GetPictureRect(), clientData.mYSize,
+ clientData.mYStride, clientData.mCbCrSize, clientData.mCbCrStride,
+ StereoMode::MONO, gfx::ColorDepth::COLOR_8, gfx::YUVColorSpace::BT601,
+ gfx::ColorRange::LIMITED, TextureFlags::DEALLOCATE_CLIENT);
+ }
+
+#ifdef XP_WIN
+ RefPtr<ID3D11Device> device = DeviceManagerDx::Get()->GetImageDevice();
+
+ if (device && aLayersBackend == LayersBackend::LAYERS_D3D11) {
+ DXGIYCbCrTextureAllocationHelper helper(clientData, TextureFlags::DEFAULT,
+ device);
+ RefPtr<TextureClient> texture = helper.Allocate(nullptr);
+ return texture.forget();
+ }
+#endif
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, TextureFlags::DEALLOCATE_CLIENT,
+ nullptr);
+ }
+
+ return nullptr;
+}
+
+/**
+ * Create a TextureClient according to the given backend.
+ */
+static already_AddRefed<TextureClient> CreateTextureClientWithBackend(
+ LayersBackend aLayersBackend) {
+ TextureData* data = nullptr;
+ SurfaceFormat format = gfxPlatform::GetPlatform()->Optimal2DFormatForContent(
+ gfxContentType::COLOR_ALPHA);
+ BackendType moz2DBackend =
+ gfxPlatform::GetPlatform()->GetContentBackendFor(aLayersBackend);
+ TextureAllocationFlags allocFlags = TextureAllocationFlags::ALLOC_DEFAULT;
+ IntSize size = IntSize(400, 300);
+ TextureFlags textureFlags = TextureFlags::DEALLOCATE_CLIENT;
+
+ if (!gfx::Factory::AllowedSurfaceSize(size)) {
+ return nullptr;
+ }
+
+#ifdef XP_WIN
+ if (aLayersBackend == LayersBackend::LAYERS_D3D11 &&
+ (moz2DBackend == BackendType::DIRECT2D ||
+ moz2DBackend == BackendType::DIRECT2D1_1)) {
+ // Create D3D11TextureData.
+ data = D3D11TextureData::Create(size, format, allocFlags);
+ } else if (!data && format == SurfaceFormat::B8G8R8X8 &&
+ moz2DBackend == BackendType::CAIRO) {
+ // Create DIBTextureData.
+ data = DIBTextureData::Create(size, format, nullptr);
+ }
+#endif
+
+ if (!data && aLayersBackend == LayersBackend::LAYERS_BASIC) {
+ // Create BufferTextureData.
+ data = BufferTextureData::Create(size, format, moz2DBackend, aLayersBackend,
+ textureFlags, allocFlags, nullptr);
+ }
+
+ if (data) {
+ return MakeAndAddRef<TextureClient>(data, textureFlags, nullptr);
+ }
+
+ return nullptr;
+}
+
+/**
+ * Create a TextureHost according to the given TextureClient.
+ */
+already_AddRefed<TextureHost> CreateTextureHostWithBackend(
+ TextureClient* aClient, ISurfaceAllocator* aDeallocator,
+ LayersBackend& aLayersBackend) {
+ if (!aClient) {
+ return nullptr;
+ }
+
+ // client serialization
+ SurfaceDescriptor descriptor;
+ ReadLockDescriptor readLock = null_t();
+ RefPtr<TextureHost> textureHost;
+
+ aClient->ToSurfaceDescriptor(descriptor);
+
+ wr::MaybeExternalImageId id = Nothing();
+ return TextureHost::Create(descriptor, readLock, aDeallocator, aLayersBackend,
+ aClient->GetFlags(), id);
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/tests/gtest/gfxSurfaceRefCountTest.cpp b/gfx/tests/gtest/gfxSurfaceRefCountTest.cpp
new file mode 100644
index 0000000000..77ab5aeb91
--- /dev/null
+++ b/gfx/tests/gtest/gfxSurfaceRefCountTest.cpp
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include <stdio.h>
+
+#include "gtest/gtest.h"
+
+#include "gfxASurface.h"
+#include "gfxImageSurface.h"
+
+#include "nsISupportsUtils.h" // for NS_ADDREF
+
+#include "cairo.h"
+
+static int GetASurfaceRefCount(gfxASurface* s) {
+ NS_ADDREF(s);
+ return s->Release();
+}
+
+static int CheckInt(int value, int expected) {
+ if (value != expected) {
+ fprintf(stderr, "Expected %d got %d\n", expected, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int CheckPointer(void* value, void* expected) {
+ if (value != expected) {
+ fprintf(stderr, "Expected %p got %p\n", expected, value);
+ return 1;
+ }
+
+ return 0;
+}
+
+static cairo_user_data_key_t destruction_key;
+static void SurfaceDestroyNotifier(void* data) { *(int*)data = 1; }
+
+static int TestNewSurface() {
+ int failures = 0;
+ int destroyed = 0;
+
+ RefPtr<gfxASurface> s =
+ new gfxImageSurface(mozilla::gfx::IntSize(10, 10),
+ mozilla::gfx::SurfaceFormat::A8R8G8B8_UINT32);
+ cairo_surface_t* cs = s->CairoSurface();
+
+ cairo_surface_set_user_data(cs, &destruction_key, &destroyed,
+ SurfaceDestroyNotifier);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 1);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_reference(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ gfxASurface* savedWrapper = s.get();
+
+ s = nullptr;
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ s = gfxASurface::Wrap(cs);
+
+ failures += CheckPointer(s.get(), savedWrapper);
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 1);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ s = nullptr;
+
+ failures += CheckInt(destroyed, 1);
+
+ return failures;
+}
+
+static int TestExistingSurface() {
+ int failures = 0;
+ int destroyed = 0;
+
+ cairo_surface_t* cs = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 10, 10);
+
+ cairo_surface_set_user_data(cs, &destruction_key, &destroyed,
+ SurfaceDestroyNotifier);
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ RefPtr<gfxASurface> s = gfxASurface::Wrap(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+
+ cairo_surface_reference(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 3);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 3);
+ failures += CheckInt(destroyed, 0);
+
+ gfxASurface* savedWrapper = s.get();
+
+ s = nullptr;
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ s = gfxASurface::Wrap(cs);
+
+ failures += CheckPointer(s.get(), savedWrapper);
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 3);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 3);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt(GetASurfaceRefCount(s.get()), 2);
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 2);
+ failures += CheckInt(destroyed, 0);
+
+ s = nullptr;
+
+ failures += CheckInt(cairo_surface_get_reference_count(cs), 1);
+ failures += CheckInt(destroyed, 0);
+
+ cairo_surface_destroy(cs);
+
+ failures += CheckInt(destroyed, 1);
+
+ return failures;
+}
+
+TEST(Gfx, SurfaceRefCount)
+{
+ int fail;
+
+ fail = TestNewSurface();
+ EXPECT_TRUE(fail == 0) << "TestNewSurface: " << fail << " failures";
+ fail = TestExistingSurface();
+ EXPECT_TRUE(fail == 0) << "TestExistingSurface: " << fail << " failures";
+}
diff --git a/gfx/tests/gtest/moz.build b/gfx/tests/gtest/moz.build
new file mode 100644
index 0000000000..450910d833
--- /dev/null
+++ b/gfx/tests/gtest/moz.build
@@ -0,0 +1,91 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+UNIFIED_SOURCES += [
+ "gfxSurfaceRefCountTest.cpp",
+ "MockWidget.cpp",
+ "PolygonTestUtils.cpp",
+ "TestArena.cpp",
+ "TestArrayView.cpp",
+ "TestBSPTree.cpp",
+ "TestBufferRotation.cpp",
+ "TestColorNames.cpp",
+ "TestConfigManager.cpp",
+ "TestGfxWidgets.cpp",
+ "TestLayers.cpp",
+ "TestMatrix.cpp",
+ "TestMoz2D.cpp",
+ "TestPolygon.cpp",
+ "TestQcms.cpp",
+ "TestRegion.cpp",
+ "TestSkipChars.cpp",
+ "TestSwizzle.cpp",
+ "TestTextures.cpp",
+ "TestTreeTraversal.cpp",
+]
+
+# skip the test on windows10-aarch64 due to perma-crash - bug 1544961
+if not (CONFIG["OS_TARGET"] == "WINNT" and CONFIG["CPU_ARCH"] == "aarch64"):
+ UNIFIED_SOURCES += [
+ "TestVsync.cpp",
+ ]
+
+if CONFIG["OS_TARGET"] != "Android":
+ UNIFIED_SOURCES += [
+ "TestCompositor.cpp",
+ "TestRect.cpp",
+ "TestTextureCompatibility.cpp",
+ ]
+
+UNIFIED_SOURCES += [
+ "/gfx/2d/unittest/%s" % p
+ for p in [
+ "TestBase.cpp",
+ "TestBugs.cpp",
+ "TestCairo.cpp",
+ "TestPoint.cpp",
+ "TestScaling.cpp",
+ ]
+]
+
+# not UNIFIED_SOURCES because layout_common_table_test.cc has classes
+# in an anonymous namespace which result in a GCC error when used in
+# tests (e g. "error: 'ScriptListTableTest_TestSuccess_Test' has a field
+# 'ScriptListTableTest_TestSuccess_Test::<anonymous>' whose type uses
+# the anonymous namespace").
+SOURCES += [
+ "/gfx/ots/tests/%s" % p
+ for p in [
+ "cff_charstring_test.cc",
+ "layout_common_table_test.cc",
+ ]
+]
+
+# ICC profiles used for verifying QCMS transformations. The copyright
+# notice embedded in the profiles should be reviewed to ensure there are
+# no known restrictions on distribution.
+TEST_HARNESS_FILES.gtest += [
+ "../../qcms/profiles/lcms_samsung_syncmaster.icc",
+ "../../qcms/profiles/lcms_thinkpad_w540.icc",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+LOCAL_INCLUDES += [
+ "/gfx/2d",
+ "/gfx/2d/unittest",
+ "/gfx/config",
+ "/gfx/layers",
+ "/gfx/ots/src",
+ "/gfx/qcms",
+]
+
+FINAL_LIBRARY = "xul-gtest"
+
+CXXFLAGS += CONFIG["MOZ_CAIRO_CFLAGS"]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]
diff --git a/gfx/tests/mochitest/mochitest.ini b/gfx/tests/mochitest/mochitest.ini
new file mode 100644
index 0000000000..72725d3741
--- /dev/null
+++ b/gfx/tests/mochitest/mochitest.ini
@@ -0,0 +1,9 @@
+[DEFAULT]
+
+[test_acceleration.html]
+skip-if = (os == 'win') # Bug 1430530
+subsuite = gpu
+[test_bug509244.html]
+[test_bug513439.html]
+[test_font_whitelist.html]
+skip-if = debug || asan # Race between pref service and gfx platform IPC causes frequent failures on debug/ASan
diff --git a/gfx/tests/mochitest/test_acceleration.html b/gfx/tests/mochitest/test_acceleration.html
new file mode 100644
index 0000000000..dd6e146baf
--- /dev/null
+++ b/gfx/tests/mochitest/test_acceleration.html
@@ -0,0 +1,163 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=627498
+-->
+<head>
+ <title>Test hardware acceleration</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=627498">Mozilla Bug 627498</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+// Make sure that acceleration is enabled/disabled the way we expect it to be
+// based on platform.
+
+SimpleTest.waitForExplicitFinish();
+
+addEventListener("pageshow", runTest, false);
+
+function runTest() {
+ var Cc = SpecialPowers.Cc;
+ var Ci = SpecialPowers.Ci;
+
+ var sysInfo = SpecialPowers.Services.sysinfo;
+ var xr = SpecialPowers.Services.appinfo;
+
+ var windows = SpecialPowers.Services.ww.getWindowEnumerator();
+ var windowutils;
+ var acceleratedWindows = 0;
+ var advancedLayersWindows = 0;
+ var webrenderWindows = 0;
+ var layerManagerLog = [];
+ while (windows.hasMoreElements()) {
+ windowutils = windows.getNext().windowUtils;
+ try {
+ if (windowutils.layerManagerType != "Basic") {
+ acceleratedWindows++;
+ }
+ if (windowutils.layerManagerType == "WebRender") {
+ webrenderWindows++;
+ }
+ if (windowutils.usingAdvancedLayers) {
+ advancedLayersWindows++;
+ }
+ layerManagerLog.push(windowutils.layerManagerType + ":" +
+ windowutils.usingAdvancedLayers);
+ } catch (e) {
+ // The window may not have a layer manager, in which case we get an error.
+ // Don't count it as an accelerated window.
+ dump("Didn't get a layer manager! " + e);
+ }
+ }
+
+ var osName = sysInfo.getProperty("name");
+ switch (osName) {
+ case "Darwin": // Mac OS X.
+ // We only enable OpenGL layers on machines that don't support QuickDraw
+ // plugins. x86-64 architecture is a good proxy for this plugin support.
+ if (sysInfo.getProperty("arch") != "x86-64") {
+ is(acceleratedWindows, 0, "Acceleration not supported on x86 OS X");
+ } else {
+ // Workaround for SeaMonkey tinderboxes which don't support acceleration.
+ if (navigator.userAgent.match(/ SeaMonkey\//)) {
+ if (acceleratedWindows == 0) {
+ todo(false, "Acceleration not supported on x86-64 OS X" +
+ " (This is expected on SeaMonkey (tinderboxes).)");
+ break;
+ }
+ }
+
+ isnot(acceleratedWindows, 0, "Acceleration enabled on x86-64 OS X");
+ }
+ break;
+
+ case "Windows_NT": // Windows.
+ var version = parseFloat(sysInfo.getProperty("version"));
+ if (version == 5.0) {
+ is(acceleratedWindows, 0, "Acceleration not supported on Windows 2000");
+ } else {
+ // Workaround for SeaMonkey tinderboxes which don't support acceleration.
+ if (navigator.userAgent.match(/ SeaMonkey\//)) {
+ if (acceleratedWindows == 0) {
+ todo(false, "Acceleration not supported on Windows XP or newer" +
+ " (This is expected on SeaMonkey (tinderboxes).)");
+ break;
+ }
+ }
+
+ isnot(acceleratedWindows, 0, "Acceleration enabled on Windows XP or newer");
+ }
+
+ var gfxInfo = Cc["@mozilla.org/gfx/info;1"].getService(Ci.nsIGfxInfo);
+ if (version < 6.2) {
+ ok(!gfxInfo.D2DEnabled, "Direct2D not supported on Windows 2008 or older");
+ if (version < 6.1) {
+ ok(!gfxInfo.DWriteEnabled, "DirectWrite not supported on Windows 2008 or older");
+ } else {
+ ok(gfxInfo.DWriteEnabled, "DirectWrite enabled on Windows 7 or newer");
+ }
+ } else {
+ ok(gfxInfo.D2DEnabled, "Direct2D enabled on Windows 8 or newer");
+ ok(gfxInfo.DWriteEnabled, "DirectWrite enabled on Windows 7 or newer");
+ }
+
+ var shouldGetWR = false;
+ try {
+ shouldGetWR = SpecialPowers.DOMWindowUtils.isWebRenderRequested;
+ } catch (e) {}
+
+ var advancedLayersEnabled = false;
+ var advancedLayersEnabledOnWin7 = false;
+ try {
+ advancedLayersEnabled = SpecialPowers.getBoolPref("layers.mlgpu.enabled");
+ advancedLayersEnabledOnWin7 = SpecialPowers.getBoolPref("layers.mlgpu.enable-on-windows7");
+ } catch (e) {}
+ var shouldGetAL = advancedLayersEnabled;
+ if (version < 6.2) {
+ shouldGetAL &= advancedLayersEnabledOnWin7;
+ }
+ if (shouldGetWR) {
+ shouldGetAL = false;
+ }
+
+ if (shouldGetAL) {
+ isnot(advancedLayersWindows, 0, "Advanced Layers enabled on Windows; "
+ + layerManagerLog.join(","));
+ } else {
+ is(advancedLayersWindows, 0, "Advanced Layers disabled on Windows");
+ }
+
+ if (shouldGetWR) {
+ isnot(webrenderWindows, 0, "WebRender enabled on Windows");
+ } else {
+ is(webrenderWindows, 0, "WebRender disabled on Windows");
+ }
+ break;
+
+ case "Linux":
+ todo(false, "Acceleration supported on Linux, but only on taskcluster instances (bug 1296086)");
+ break;
+
+ default:
+ if (xr.OS == "Android") {
+ isnot(acceleratedWindows, 0, "Acceleration enabled on Android");
+ } else {
+ is(acceleratedWindows, 0, "Acceleration not supported on '" + osName + "'");
+ }
+ }
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/gfx/tests/mochitest/test_bug509244.html b/gfx/tests/mochitest/test_bug509244.html
new file mode 100644
index 0000000000..f7348909c2
--- /dev/null
+++ b/gfx/tests/mochitest/test_bug509244.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=509244
+-->
+<head>
+ <title>Test for Bug 509244</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=509244">Mozilla Bug 509244</a>
+<p id="display">A</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 509244 **/
+
+function flush() { document.documentElement.offsetHeight; }
+
+var text = document.getElementById("display");
+
+// layout text, caching monospace font
+text.style.fontFamily = "monospace";
+flush();
+// relayout text so that monospace font is no longer used (but cached)
+text.style.fontFamily = "sans-serif";
+flush();
+
+// flush cache
+SpecialPowers.Services.obs.notifyObservers(null, "memory-pressure", "heap-minimize");
+
+// reuse font that was flushed from cache
+text.style.fontFamily = "monospace";
+flush();
+
+ok(true, "not crashed");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/gfx/tests/mochitest/test_bug513439.html b/gfx/tests/mochitest/test_bug513439.html
new file mode 100644
index 0000000000..6672ae97aa
--- /dev/null
+++ b/gfx/tests/mochitest/test_bug513439.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=513439
+-->
+<head>
+ <title>Test for Bug 513439</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=513439">Mozilla Bug 513439</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 513439 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var domWindowUtils = SpecialPowers.DOMWindowUtils;
+SpecialPowers.pushPrefEnv({set: [["layout.css.devPixelsPerPx", "2"]]}, () => {
+ is(domWindowUtils.screenPixelsPerCSSPixel, 2, "devPixelsPerPx wasn't set correctly");
+
+ SpecialPowers.pushPrefEnv({set: [["layout.css.devPixelsPerPx", "1.5"]]}, () => {
+ is(domWindowUtils.screenPixelsPerCSSPixel, 1.5, "devPixelsPerPx wasn't set correctly");
+ SimpleTest.finish();
+ });
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/gfx/tests/mochitest/test_font_whitelist.html b/gfx/tests/mochitest/test_font_whitelist.html
new file mode 100644
index 0000000000..78dd755d54
--- /dev/null
+++ b/gfx/tests/mochitest/test_font_whitelist.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1121643
+-->
+<head>
+ <title>Test for Bug 1121643</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1121643">Mozilla Bug 1121643</a>
+<span id="mono" style="font-family: monospace; font-size: 64px;">M</span>
+<span id="sans" style="font-family: sans-serif; font-size: 64px;">M</span>
+<span id="serif" style="font-family: serif; font-size: 64px;">M</span>
+<div id="content" style="display: none">
+
+</div>
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 1121643 **/
+
+const InspectorUtils = SpecialPowers.InspectorUtils;
+
+// Given an element id, returns the first font face name encountered.
+let fontUsed = id => {
+ let element = document.getElementById(id),
+ range = document.createRange();
+ range.selectNode(element);
+ return InspectorUtils.getUsedFontFaces(range)[0].CSSFamilyName;
+};
+
+// A map of the default mono, sans and serif fonts, obtained when
+// whitelisting is disabled.
+const fonts = { mono: fontUsed("mono"),
+ sans: fontUsed("sans"),
+ serif: fontUsed("serif") };
+
+let hack = 0;
+
+// Set the font whitelist to contain none, some, or all of the
+// default mono, sans, and serif fonts. Check that the rendering
+// of our three test elements uses only fonts present in the
+// whitelist.
+let testFontWhitelist = async function(useMono, useSans, useSerif) {
+ let whitelist = [];
+ if (useMono) {
+ whitelist.push(fonts.mono);
+ }
+ if (useSans) {
+ whitelist.push(fonts.sans);
+ }
+ if (useSerif) {
+ whitelist.push(fonts.serif);
+ }
+ await SpecialPowers.pushPrefEnv({"set": [["font.system.whitelist",
+ whitelist.join(", ")]]});
+
+ await new Promise(SimpleTest.executeSoon);
+ await SpecialPowers.setIntPref("font.fixme.hack", hack++);
+ await new Promise(SimpleTest.executeSoon);
+
+ // If whitelist is empty, then whitelisting is considered disabled
+ // and all fonts are allowed.
+ info("font whitelist: " + JSON.stringify(whitelist));
+ let whitelistEmpty = whitelist.length === 0;
+ is(useMono || whitelistEmpty, fontUsed("mono") === fonts.mono,
+ "Correct mono whitelisting state; got " + fontUsed("mono") + ", requested " + fonts.mono);
+ is(useSans || whitelistEmpty, fontUsed("sans") === fonts.sans,
+ "Correct sans whitelisting state; got " + fontUsed("sans") + ", requested " + fonts.sans);
+ is(useSerif || whitelistEmpty, fontUsed("serif") === fonts.serif,
+ "Correct serif whitelisting state; got " + fontUsed("serif") + ", requested " + fonts.serif);
+};
+
+// Run tests to confirm that only whitelisting fonts are present in a
+// rendered page. Try turning mono, sans, and serif off and on in
+// every combination.
+add_task(async function() {
+ for (let useMono of [false, true]) {
+ for (let useSans of [false, true]) {
+ for (let useSerif of [false, true]) {
+ await testFontWhitelist(useMono, useSans, useSerif);
+ }
+ }
+ }
+});
+
+</script>
+</body>
+</html>
diff --git a/gfx/tests/moz.build b/gfx/tests/moz.build
new file mode 100644
index 0000000000..83e02d043e
--- /dev/null
+++ b/gfx/tests/moz.build
@@ -0,0 +1,9 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+MOCHITEST_MANIFESTS += ["mochitest/mochitest.ini"]
+BROWSER_CHROME_MANIFESTS += ["browser/browser.ini"]
+MOCHITEST_CHROME_MANIFESTS += ["chrome/chrome.ini"]
diff --git a/gfx/tests/reftest/1086723-ref.html b/gfx/tests/reftest/1086723-ref.html
new file mode 100644
index 0000000000..0242f9e96b
--- /dev/null
+++ b/gfx/tests/reftest/1086723-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Testcase for bug 1086723</title>
+ <style type="text/css">
+ .test_div {
+ position: fixed;
+ overflow: hidden;
+ background: blue;
+ width: 50%;
+ height: 50%;
+ border-radius: 0px 50% 50% 0px;
+ }
+ .filler {
+ height: 5000px;
+ }
+ body,html {
+ overflow: hidden;
+ }
+ </style>
+</head>
+<body>
+ <div class="test_div"></div>
+ <div class="filler"></div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1086723.html b/gfx/tests/reftest/1086723.html
new file mode 100644
index 0000000000..05ac2d7d89
--- /dev/null
+++ b/gfx/tests/reftest/1086723.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html reftest-async-scroll reftest-async-scroll-x="0" reftest-async-scroll-y="2000">
+<head>
+ <meta charset="utf-8">
+ <title>Testcase for bug 1086723</title>
+ <style type="text/css">
+ .test_div {
+ position: fixed;
+ overflow: hidden;
+ background: blue;
+ width: 50%;
+ height: 50%;
+ border-radius: 0px 50% 50% 0px;
+ }
+ .filler {
+ height: 5000px;
+ }
+ body,html {
+ scrollbar-width: none;
+ }
+ </style>
+</head>
+<body>
+ <div class="test_div"></div>
+ <div class="filler"></div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1131264-1.svg b/gfx/tests/reftest/1131264-1.svg
new file mode 100644
index 0000000000..fdafd6b134
--- /dev/null
+++ b/gfx/tests/reftest/1131264-1.svg
@@ -0,0 +1,17 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg"
+ width="100%" height="100%">
+
+ <title>Testcase for small radius circle with large center coordinates</title>
+ <!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1131264 -->
+
+ <rect width="100%" height="100%" fill="lime"/>
+
+ <circle r="5" cx="40" cy="40" fill="red" />
+ <circle r="1" cx="10004" cy="10004" fill="lime"
+ transform="scale(10 10) translate(-10000 -10000)"/>
+
+</svg>
diff --git a/gfx/tests/reftest/1143303-1.svg b/gfx/tests/reftest/1143303-1.svg
new file mode 100644
index 0000000000..4654cff5c0
--- /dev/null
+++ b/gfx/tests/reftest/1143303-1.svg
@@ -0,0 +1,26 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ width="100%" height="100%">
+ <title>Testcase for small circles</title>
+ <!--From https://bugzilla.mozilla.org/show_bug.cgi?id=1143303 -->
+
+ <rect width="100%" height="100%" fill="lime"/>
+
+ <circle cx="200" cy="150" r="95" fill="red"/>
+ <g transform="translate(200, 150)" fill="lime">
+ <g transform="scale(1e8, 1e8)">
+ <circle cx="0" cy="0" r="1e-6"/>
+ </g>
+ </g>
+
+ <circle cx="342" cy="176.06" r="1" fill="red"/>
+ <g transform="translate(342,1098.55)" fill="lime">
+ <g transform="scale(418.2,-405.9)">
+ <circle cx="0" cy="2.2727" r=".006"/>
+ </g>
+ </g>
+
+</svg>
diff --git a/gfx/tests/reftest/1149923-ref.html b/gfx/tests/reftest/1149923-ref.html
new file mode 100644
index 0000000000..46625a786d
--- /dev/null
+++ b/gfx/tests/reftest/1149923-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
+ <meta charset="utf-8">
+ <title>Testcase for bug 1149923</title>
+ <style>
+ #outer {
+ width: 64px;
+ height: 64px;
+ background-color: #00f;
+ opacity: 1.0;
+ transform: rotate(90deg);
+ }
+ #inner {
+ width: 100%;
+ height: 100%;
+ background-color: #f00;
+ border-top-left-radius: 10%;
+ border-bottom-left-radius: 10%;
+ }
+ </style>
+ </head>
+ <body>
+ <div id='outer'><div id='inner'></div></div>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1149923.html b/gfx/tests/reftest/1149923.html
new file mode 100644
index 0000000000..ec5f777ad3
--- /dev/null
+++ b/gfx/tests/reftest/1149923.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1">
+ <meta charset="utf-8">
+ <title>Testcase for bug 1149923</title>
+ <style>
+ #outer {
+ width: 64px;
+ height: 64px;
+ background-color: #00f;
+ opacity: 1.0;
+ transform: rotate(90deg);
+ }
+ #inner {
+ width: 100%;
+ height: 100%;
+ background-color: #f00;
+ will-change: transform;
+ border-top-left-radius: 10%;
+ border-bottom-left-radius: 10%;
+ }
+ </style>
+ </head>
+ <body>
+ <div id='outer'><div id='inner'></div></div>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1419528-ref.html b/gfx/tests/reftest/1419528-ref.html
new file mode 100644
index 0000000000..2a3595b9af
--- /dev/null
+++ b/gfx/tests/reftest/1419528-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+.outer {
+ display: flex;
+}
+.inner {
+ padding-bottom: 6px;
+ padding-top: 10 px;
+}
+</style>
+<body>
+ <div class="outer">
+ <ul class="inner">
+ Testing
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1419528.html b/gfx/tests/reftest/1419528.html
new file mode 100644
index 0000000000..a9c65b3914
--- /dev/null
+++ b/gfx/tests/reftest/1419528.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<style>
+.outer {
+ display: flex;
+}
+.inner {
+ padding-bottom: 6px;
+ padding-top: 10 px;
+ box-shadow: inset 0 0 0 0 #000;
+}
+</style>
+<body>
+ <div class="outer">
+ <ul class="inner">
+ Testing
+ </ul>
+ </div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1424673-ref.html b/gfx/tests/reftest/1424673-ref.html
new file mode 100644
index 0000000000..5e227d709d
--- /dev/null
+++ b/gfx/tests/reftest/1424673-ref.html
@@ -0,0 +1,6 @@
+<html>
+<body>
+<div style="width: 200px; height: 200px; background-color: lime;">
+</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1424673.html b/gfx/tests/reftest/1424673.html
new file mode 100644
index 0000000000..31384741b5
--- /dev/null
+++ b/gfx/tests/reftest/1424673.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<style>
+body {
+ overflow:hidden;
+}
+
+.container {
+ background-color: lime;
+}
+
+.text_shadow {
+ color: #fff;
+ text-shadow: 0px 0px 10px #fff;
+ line-height: 0;
+ top: 300px;
+ width: 300px;
+ height: 300px;
+ font-size: 300px;
+}
+</style>
+<script>
+function shadow(){
+ document.write('<div class="text_shadow" style="position: absolute; top: 30px; left: 10px;">.</div>');
+ document.write('<div class="text_shadow" style="position: absolute; top: 30px; left: 10px;">.</div>');
+ document.write('<div class="text_shadow" style="position: absolute; top: 30px; left: 10px;">.</div>');
+}
+
+function run_Test(){
+ document.write('<div style="position: relative; top: 160px; left: 0px;"><script>shadow();<\/script></div>');
+ document.write('<div style="position: relative; top: 160px; left: -90px;"><script>shadow();<\/script></div>');
+ document.write('<div style="position: relative; top: 160px; left: 70px;"><script>shadow();<\/script></div>');
+ document.documentElement.classList.remove("reftest-wait");
+}
+
+run_Test();
+</script>
+<div class="container" style="width: 200px; height: 200px;">
+</html>
diff --git a/gfx/tests/reftest/1429411-ref.html b/gfx/tests/reftest/1429411-ref.html
new file mode 100644
index 0000000000..72dcdcfb61
--- /dev/null
+++ b/gfx/tests/reftest/1429411-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<style>
+
+.outer {
+ position: absolute;
+ width: 200px;
+ height: 150px;
+ overflow: hidden;
+}
+.inner {
+ position: absolute;
+ top: 3px;
+ left: 3px;
+ width: 200px;
+ height: 150px;
+ box-shadow: blue 0px 0px 20px inset;
+}
+
+</style>
+
+<div class="outer">
+<div class="inner"></div>
+</div>
diff --git a/gfx/tests/reftest/1429411.html b/gfx/tests/reftest/1429411.html
new file mode 100644
index 0000000000..79b878a6db
--- /dev/null
+++ b/gfx/tests/reftest/1429411.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html lang="en">
+<meta charset="utf-8">
+<style>
+
+.outer {
+ position: absolute;
+ margin-top: 3px;
+ margin-left: 3px;
+ width: 200px;
+ height: 150px;
+ overflow: hidden;
+}
+.inner {
+ position: absolute;
+ margin-top: -3px;
+ margin-left: -3px;
+ width: 200px;
+ height: 150px;
+ box-shadow: blue 3px 3px 20px inset;
+}
+
+</style>
+
+<div class="outer">
+<div class="inner"></div>
+</div>
diff --git a/gfx/tests/reftest/1435143-ref.html b/gfx/tests/reftest/1435143-ref.html
new file mode 100644
index 0000000000..f5d1d4de8e
--- /dev/null
+++ b/gfx/tests/reftest/1435143-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ #green {
+ position: absolute;
+ background: green;
+ border-radius: 1px;
+ transform: translateX(100px);
+ }
+ #text {
+ visibility: hidden;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="header">
+ <div id="green"><span id="text">Text.</span></div>
+ </div>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1435143.html b/gfx/tests/reftest/1435143.html
new file mode 100644
index 0000000000..3e209f05dd
--- /dev/null
+++ b/gfx/tests/reftest/1435143.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ #header {
+ position: fixed;
+ }
+ #green {
+ position: absolute;
+ background: green;
+ border-radius: 1px;
+ transform: translateX(100px);
+ }
+ #text {
+ visibility: hidden;
+ }
+ </style>
+ </head>
+
+ <body>
+ <div id="header">
+ <div id="green"><span id="text">Text.</span></div>
+ </div>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1444904-ref.html b/gfx/tests/reftest/1444904-ref.html
new file mode 100644
index 0000000000..c41c180796
--- /dev/null
+++ b/gfx/tests/reftest/1444904-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ #box {
+ position: absolute;
+ top: 80px;
+ width: 20px;
+ height: 20px;
+ background: green;
+ }
+ </style>
+ </head>
+
+ <body style="height: 10000px;">
+ <div id="box"></div>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1444904.html b/gfx/tests/reftest/1444904.html
new file mode 100644
index 0000000000..c9fe387e74
--- /dev/null
+++ b/gfx/tests/reftest/1444904.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+ #container {
+ float: left;
+ position: sticky;
+ top: 0;
+ margin-top: -20px;
+ z-index: 100;
+ }
+
+ #target {
+ margin-top: 80px;
+ width: 20px;
+ height: 20px;
+ background: green;
+ }
+
+ #necessary-fixed-box {
+ position: fixed;
+ top: 0;
+ width: 20px;
+ height: 20px;
+
+ }
+ </style>
+ </head>
+
+ <body style="height: 10000px;">
+ <div id="container">
+ <div id="target"></div>
+ <div id="necessary-fixed-box"></div>
+ </div>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1451168-ref.html b/gfx/tests/reftest/1451168-ref.html
new file mode 100644
index 0000000000..7aa35b9245
--- /dev/null
+++ b/gfx/tests/reftest/1451168-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style type="text/css">
+html {
+ height:100%;
+ margin:0;
+ padding: 0;
+}
+body {
+ height:100%;
+ margin: 0;
+ padding: 0;
+ overflow:hidden;
+ position:relative;
+ left:0;
+}
+nav {
+ display:block;
+ position:absolute;
+ top:0;
+ left:0;
+ bottom:auto;
+ width:280px;
+ height:100%;
+ z-index:1000;
+}
+
+.nav-menu-inner-container {
+ position:relative;
+ height:100%;
+}
+
+.nav-menu-scroll-pane {
+ bottom:100px;
+ overflow-y:scroll;
+ position:absolute;
+ top:0;
+ width:254px
+}
+ </style>
+</head>
+
+<body>
+<nav class="moz-global-nav-drawer">
+ <div class="nav-menu-inner-container">
+ <div class="nav-menu-scroll-pane">
+ <div style="height: 5000px">
+ Scroll here and look for the scrollbar
+ </div>
+ </div>
+ </div>
+</nav>
+</body></html>
diff --git a/gfx/tests/reftest/1451168.html b/gfx/tests/reftest/1451168.html
new file mode 100644
index 0000000000..8a0711be44
--- /dev/null
+++ b/gfx/tests/reftest/1451168.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style type="text/css">
+html {
+ height:100%;
+ margin:0;
+ padding: 0;
+}
+body {
+ height:100%;
+ margin: 0;
+ padding: 0;
+ overflow:hidden;
+ position:relative;
+ left:0;
+ transform:translateX(320px);
+}
+nav {
+ display:block;
+ position:absolute;
+ top:0;
+ left:0;
+ bottom:auto;
+ width:280px;
+ height:100%;
+ z-index:1000;
+ transform:translateX(-320px);
+}
+
+.nav-menu-inner-container {
+ position:relative;
+ height:100%;
+}
+
+.nav-menu-scroll-pane {
+ bottom:100px;
+ overflow-y:scroll;
+ position:absolute;
+ top:0;
+ width:254px
+}
+ </style>
+</head>
+
+<body>
+<nav class="moz-global-nav-drawer">
+ <div class="nav-menu-inner-container">
+ <div class="nav-menu-scroll-pane">
+ <div style="height: 5000px">
+ Scroll here and look for the scrollbar
+ </div>
+ </div>
+ </div>
+</nav>
+</body></html>
diff --git a/gfx/tests/reftest/1461313-ref.html b/gfx/tests/reftest/1461313-ref.html
new file mode 100644
index 0000000000..14a5f811ce
--- /dev/null
+++ b/gfx/tests/reftest/1461313-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<body>
+ <div style="background-color: green; width: 100px; height: 100px;"></div>
+</body>
diff --git a/gfx/tests/reftest/1461313.html b/gfx/tests/reftest/1461313.html
new file mode 100644
index 0000000000..71e492d40d
--- /dev/null
+++ b/gfx/tests/reftest/1461313.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<style>
+body {
+ clip-path: url(non-existent-resource);
+}
+</style>
+<body>
+ <div style="background-color: green; width: 100px; height: 100px;"></div>
+</body>
diff --git a/gfx/tests/reftest/1463802-ref.html b/gfx/tests/reftest/1463802-ref.html
new file mode 100644
index 0000000000..840263e493
--- /dev/null
+++ b/gfx/tests/reftest/1463802-ref.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html><head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Clock</title>
+ <style type="text/css">
+ * {
+ box-sizing: border-box !important;
+ }
+ .inner {
+ width: 100%;
+ height: 100%;
+ background: white;
+ border-radius: 100%;
+ box-shadow: 0px 0px 18px black inset;
+ }
+ </style>
+ </head>
+ <body>
+ <div style="width: 588px; height: 588px; padding: 26px;">
+<div class="inner"></div>
+ </div>
+</body></html>
diff --git a/gfx/tests/reftest/1463802.html b/gfx/tests/reftest/1463802.html
new file mode 100644
index 0000000000..48978ba16a
--- /dev/null
+++ b/gfx/tests/reftest/1463802.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html><head>
+ <meta http-equiv="content-type" content="text/html; charset=UTF-8">
+ <title>Clock</title>
+ <style type="text/css">
+ * {
+ box-sizing: border-box !important;
+ }
+ .inner {
+ width: 100%;
+ height: 100%;
+ background: white;
+ border-radius: 100%;
+ box-shadow: 0px 0px 18px black inset;
+ }
+ </style>
+ </head>
+ <body>
+ <div style="width: 588px; height: 588px; padding: 26.01px;">
+<div class="inner"></div>
+ </div>
+</body></html>
diff --git a/gfx/tests/reftest/1474722-ref.html b/gfx/tests/reftest/1474722-ref.html
new file mode 100644
index 0000000000..e0fadcf53f
--- /dev/null
+++ b/gfx/tests/reftest/1474722-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <style>
+ .shadowed {
+ box-shadow: 0 0 35px rgba(0, 0, 0, .9);
+ border: 1px solid lightgray;
+ margin: 0 2em;
+ float: left;
+ }
+ </style>
+</head>
+<body style = "overflow:hidden">
+<div class="shadowed" style="height: 200vh"> long shadow </div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1474722.html b/gfx/tests/reftest/1474722.html
new file mode 100644
index 0000000000..9920eee238
--- /dev/null
+++ b/gfx/tests/reftest/1474722.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <style>
+ .shadowed {
+ box-shadow: 0 0 35px rgba(0, 0, 0, .9);
+ border: 1px solid lightgray;
+ margin: 0 2em;
+ float: left;
+ }
+ </style>
+</head>
+<body style = "overflow:hidden">
+<div class="shadowed" style="height: 100000px"> long shadow </div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1501195-ref.html b/gfx/tests/reftest/1501195-ref.html
new file mode 100644
index 0000000000..45ea08d565
--- /dev/null
+++ b/gfx/tests/reftest/1501195-ref.html
@@ -0,0 +1,30 @@
+<html reftest-zoom="1.1">
+<head>
+<style type="text/css">
+.parent {
+display: flex;
+overflow: hidden;
+}
+
+.bg {
+background-color: lime;
+width: 200px;
+height: 200px;
+transform: scale(1.05);
+}
+
+.bg_overlay {
+background-color: red;
+width: 100px;
+height: 100px;
+position: absolute;
+}
+</style>
+</head>
+<body>
+<div class="parent">
+ <div class="bg"></div>
+ <div class="bg_overlay"></div>
+</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1501195.html b/gfx/tests/reftest/1501195.html
new file mode 100644
index 0000000000..f1664e54ff
--- /dev/null
+++ b/gfx/tests/reftest/1501195.html
@@ -0,0 +1,31 @@
+<html reftest-zoom="1.1">
+<head>
+<style type="text/css">
+.parent {
+display: flex;
+transform: translate3d(0px, 0px, 0px);
+overflow: hidden;
+}
+
+.bg {
+background-color: lime;
+width: 200px;
+height: 200px;
+transform: scale(1.05);
+}
+
+.bg_overlay {
+background-color: red;
+width: 100px;
+height: 100px;
+position: absolute;
+}
+</style>
+</head>
+<body>
+<div class="parent">
+ <div class="bg"></div>
+ <div class="bg_overlay"></div>
+</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/1519754-ref.html b/gfx/tests/reftest/1519754-ref.html
new file mode 100644
index 0000000000..4d1b6e7992
--- /dev/null
+++ b/gfx/tests/reftest/1519754-ref.html
@@ -0,0 +1,5 @@
+<body style="margin-left: 0px">
+<div style="margin-left: 200px; width: 200px; height: 100px; overflow: auto;">
+ <div style="background-color: red; height: 200px;"/>
+</div>
+</body>
diff --git a/gfx/tests/reftest/1519754.html b/gfx/tests/reftest/1519754.html
new file mode 100644
index 0000000000..7db2093cfd
--- /dev/null
+++ b/gfx/tests/reftest/1519754.html
@@ -0,0 +1,5 @@
+<body>
+<div style="left: 400px; position: fixed; transform: translateX(-200px); width: 200px; height: 100px; overflow: auto;">
+ <div style="background-color: red; height: 200px;"/>
+</div>
+</body>
diff --git a/gfx/tests/reftest/1523080-ref.html b/gfx/tests/reftest/1523080-ref.html
new file mode 100644
index 0000000000..42e0e793b2
--- /dev/null
+++ b/gfx/tests/reftest/1523080-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<style>
+html, body {
+ margin: 0;
+}
+</style>
+<div style="width: 400px; height: 400px; background-color: green"></div>
diff --git a/gfx/tests/reftest/1523080.html b/gfx/tests/reftest/1523080.html
new file mode 100644
index 0000000000..069f86d3e5
--- /dev/null
+++ b/gfx/tests/reftest/1523080.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<style>
+html, body {
+ margin: 0;
+}
+</style>
+<div id="red" style="position:absolute; width: 400px; height: 400px; background-color: red; top: 0px; left: 0px"></div>
+<div id="scroller" style="overflow:hidden; width:400px; height: 400px">
+ <div style="filter: blur(20px); width: 800px; height: 800px; background-color: green"></div>
+</div>
+<script>
+ document.getElementById('scroller').scrollTo(100, 100);
+</script>
diff --git a/gfx/tests/reftest/1523776-ref.html b/gfx/tests/reftest/1523776-ref.html
new file mode 100644
index 0000000000..3f57f8df24
--- /dev/null
+++ b/gfx/tests/reftest/1523776-ref.html
@@ -0,0 +1,13 @@
+<style>
+html, body {
+ margin: 0;
+}
+</style>
+ <div style="filter: drop-shadow(50px 50px 20px red)">
+ <div style="width:256px; height:256px; border-radius:16px; overflow:hidden">
+ <div style="width:256px; height:256px; background-color:green;"></div>
+ </div>
+ </div>
+<!-- add white divs on top to simulate the clip from the test file -->
+<div style="background-color: white; position:absolute; left: 0; top: 0; width: 100px; height: 700px"></div>
+<div style="background-color: white; position:absolute; left: 0; top: 0; width: 700px; height: 100px"></div>
diff --git a/gfx/tests/reftest/1523776.html b/gfx/tests/reftest/1523776.html
new file mode 100644
index 0000000000..76a666763d
--- /dev/null
+++ b/gfx/tests/reftest/1523776.html
@@ -0,0 +1,12 @@
+<style>
+html, body {
+ margin: 0;
+}
+</style>
+<div style="clip: rect(100px,1000px,1000px,100px); position:absolute">
+ <div style="filter: drop-shadow(50px 50px 20px red)">
+ <div style="width:256px; height:256px; border-radius:16px; overflow:hidden">
+ <div style="width:256px; height:256px; background-color:green;"></div>
+ </div>
+ </div>
+</div>
diff --git a/gfx/tests/reftest/1524261-ref.html b/gfx/tests/reftest/1524261-ref.html
new file mode 100644
index 0000000000..45d798cec8
--- /dev/null
+++ b/gfx/tests/reftest/1524261-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<html>
+<div style="overflow-y:hidden; position:absolute; top:50px; width: 200px; height: 100px; border: solid 1px black">
+</div>
diff --git a/gfx/tests/reftest/1524261.html b/gfx/tests/reftest/1524261.html
new file mode 100644
index 0000000000..33824a9038
--- /dev/null
+++ b/gfx/tests/reftest/1524261.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html reftest-async-scroll>
+<div style="overflow-y:scroll; scrollbar-width:none; position:absolute; top:50px; width: 200px; height: 100px; border: solid 1px black"
+ reftest-displayport-x="0" reftest-displayport-y="0"
+ reftest-displayport-w="200" reftest-displayport-h="300"
+ reftest-async-scroll-x="0" reftest-async-scroll-y="50">
+ <div style="transform: translateY(0px); width: 100px; height: 300px">
+ <div style="background-color: green; height: 25px; width: 25px; border-radius: 5px"></div>
+ </div>
+</div>
diff --git a/gfx/tests/reftest/1524353-ref.html b/gfx/tests/reftest/1524353-ref.html
new file mode 100644
index 0000000000..e1c5e78314
--- /dev/null
+++ b/gfx/tests/reftest/1524353-ref.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<div style="overflow:hidden; width: 300px; height: 200px; border: solid 1px black">
+ <div style="width: 600px; transform: translateX(0px); will-change: transform">
+ <svg style="height: 200px" width="600">
+ <path d="M0 0 H 300 V 25 Z" fill="red"></path>
+ </svg>
+ </div>
+</div>
diff --git a/gfx/tests/reftest/1524353.html b/gfx/tests/reftest/1524353.html
new file mode 100644
index 0000000000..72a0f7d686
--- /dev/null
+++ b/gfx/tests/reftest/1524353.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<div style="overflow:hidden; width: 300px; height: 200px; border: solid 1px black">
+ <div style="width: 600px; transform: translateX(0px); will-change: transform">
+ <svg style="height: 200px" width="600">
+ <path d="M0 0 H 600 V 50 Z" fill="red"></path>
+ </svg>
+ </div>
+</div>
diff --git a/gfx/tests/reftest/1616444-same-color-different-paths-ref.html b/gfx/tests/reftest/1616444-same-color-different-paths-ref.html
new file mode 100644
index 0000000000..80f3634bc9
--- /dev/null
+++ b/gfx/tests/reftest/1616444-same-color-different-paths-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
+ .swatch {
+ float: left;
+ height: 40px;
+ width: 120px;
+ box-sizing: border-box;
+ }
+ .swatch2 {
+ float: left;
+ height: 40px;
+ width: 720px;
+ box-sizing: border-box;
+ }
+ .header {
+ border: 1px solid black;
+ font-size: 10px;
+ font-weight: bold;
+ }
+ br { clear: both; }
+
+ .red { background: red }
+ .orange { background: orange }
+ .yellow { background: yellow }
+ .green { background: green }
+ .blue { background: blue }
+ .indigo { background: indigo }
+ .violet { background: violet }
+
+</style>
+</head><body>
+ <div class="swatch header">A: named color</div>
+ <div class="swatch header">B: �&amp; will-change: transform</div>
+ <div class="swatch header">C: �&amp; border</div>
+ <div class="swatch header">D: solid-color gradient </div>
+ <div class="swatch header">E: �&amp; will-change: transform</div>
+ <div class="swatch header">F: �&amp; border</div>
+ <br>
+ <div class="swatch2 red"></div>
+ <br>
+ <div class="swatch2 orange"></div>
+ <br>
+ <div class="swatch2 yellow"></div>
+ <br>
+ <div class="swatch2 green"></div>
+ <br>
+ <div class="swatch2 blue"></div>
+ <br>
+ <div class="swatch2 indigo"></div>
+ <br>
+ <div class="swatch2 violet"></div>
+ <br>
+</body></html>
diff --git a/gfx/tests/reftest/1616444-same-color-different-paths.html b/gfx/tests/reftest/1616444-same-color-different-paths.html
new file mode 100644
index 0000000000..be51b1d243
--- /dev/null
+++ b/gfx/tests/reftest/1616444-same-color-different-paths.html
@@ -0,0 +1,92 @@
+<!DOCTYPE html>
+<html><head>
+<meta http-equiv="content-type" content="text/html; charset=windows-1252"><style>
+ .swatch {
+ float: left;
+ height: 40px;
+ width: 120px;
+ box-sizing: border-box;
+ }
+ .header {
+ border: 1px solid black;
+ font-size: 10px;
+ font-weight: bold;
+ }
+ br { clear: both; }
+ .layer { will-change: transform }
+
+ .add-border { border-left: 1px solid transparent; }
+ .red { background: red }
+ .g-red { background: linear-gradient(red,red) }
+ .orange { background: orange }
+ .g-orange { background: linear-gradient(orange,orange) }
+ .yellow { background: yellow }
+ .g-yellow { background: linear-gradient(yellow,yellow) }
+ .green { background: green }
+ .g-green { background: linear-gradient(green,green) }
+ .blue { background: blue }
+ .g-blue { background: linear-gradient(blue,blue) }
+ .indigo { background: indigo }
+ .g-indigo { background: linear-gradient(indigo,indigo) }
+ .violet { background: violet }
+ .g-violet { background: linear-gradient(violet,violet) }
+
+</style>
+</head><body>
+ <div class="swatch header">A: named color</div>
+ <div class="swatch header">B: �&amp; will-change: transform</div>
+ <div class="swatch header">C: �&amp; border</div>
+ <div class="swatch header">D: solid-color gradient </div>
+ <div class="swatch header">E: �&amp; will-change: transform</div>
+ <div class="swatch header">F: �&amp; border</div>
+ <br>
+ <div class="swatch red"></div>
+ <div class="swatch red layer"></div>
+ <div class="swatch red layer add-border"></div>
+ <div class="swatch g-red"></div>
+ <div class="swatch g-red layer"></div>
+ <div class="swatch g-red layer add-border"></div>
+ <br>
+ <div class="swatch orange"></div>
+ <div class="swatch orange layer"></div>
+ <div class="swatch orange layer add-border"></div>
+ <div class="swatch g-orange"></div>
+ <div class="swatch g-orange layer"></div>
+ <div class="swatch g-orange layer add-border"></div>
+ <br>
+ <div class="swatch yellow"></div>
+ <div class="swatch yellow layer"></div>
+ <div class="swatch yellow layer add-border"></div>
+ <div class="swatch g-yellow"></div>
+ <div class="swatch g-yellow layer"></div>
+ <div class="swatch g-yellow layer add-border"></div>
+ <br>
+ <div class="swatch green"></div>
+ <div class="swatch green layer"></div>
+ <div class="swatch green layer add-border"></div>
+ <div class="swatch g-green"></div>
+ <div class="swatch g-green layer"></div>
+ <div class="swatch g-green layer add-border"></div>
+ <br>
+ <div class="swatch blue"></div>
+ <div class="swatch blue layer"></div>
+ <div class="swatch blue layer add-border"></div>
+ <div class="swatch g-blue"></div>
+ <div class="swatch g-blue layer"></div>
+ <div class="swatch g-blue layer add-border"></div>
+ <br>
+ <div class="swatch indigo"></div>
+ <div class="swatch indigo layer"></div>
+ <div class="swatch indigo layer add-border"></div>
+ <div class="swatch g-indigo"></div>
+ <div class="swatch g-indigo layer"></div>
+ <div class="swatch g-indigo layer add-border"></div>
+ <br>
+ <div class="swatch violet"></div>
+ <div class="swatch violet layer"></div>
+ <div class="swatch violet layer add-border"></div>
+ <div class="swatch g-violet"></div>
+ <div class="swatch g-violet layer"></div>
+ <div class="swatch g-violet layer add-border"></div>
+ <br>
+</body></html>
diff --git a/gfx/tests/reftest/1662062-1-no-blurry.html b/gfx/tests/reftest/1662062-1-no-blurry.html
new file mode 100644
index 0000000000..93542b9b1f
--- /dev/null
+++ b/gfx/tests/reftest/1662062-1-no-blurry.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html reftest-resolution="5.0">
+<head>
+ <style>
+ html {
+ scrollbar-width: none;
+ }
+ body {
+ margin: 0;
+ }
+ </style>
+</head>
+<body>
+
+<svg height="100" width="100">
+ <polygon points="50 10 55 20 70 20 60 30 65 45 50 35 35 45 40 30 30 20 45 20" stroke="green" fill="transparent" stroke-width="5"/>
+</svg>
+
+</body>
+</html>
diff --git a/gfx/tests/reftest/1662062-1-ref.html b/gfx/tests/reftest/1662062-1-ref.html
new file mode 100644
index 0000000000..ca25585df9
--- /dev/null
+++ b/gfx/tests/reftest/1662062-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <style>
+ html {
+ scrollbar-width: none;
+ }
+ body {
+ margin: 0;
+ }
+ </style>
+</head>
+<body>
+
+<svg height="500" width="500">
+ <polygon points="250 50 275 100 350 100 300 150 325 225 250 175 175 225 200 150 150 100 225 100" stroke="green" fill="transparent" stroke-width="25"/>
+</svg>
+
+</body>
+</html>
diff --git a/gfx/tests/reftest/1681610-ref.html b/gfx/tests/reftest/1681610-ref.html
new file mode 100644
index 0000000000..cb3693cc7f
--- /dev/null
+++ b/gfx/tests/reftest/1681610-ref.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin-left: 100px;
+ font-size: 500%;
+ font-family: Nimbus Sans, sans-serif;
+ }
+ ul {
+ width: 1em; /* This must to be non-zero, for the percentages to work. */
+ }
+ li {
+ /* This clip path is chosen to mask off all the rounded edges of a
+ 'disc' list item marker, to hide the anti-aliased pixels along the
+ round edges of the bullets from the reftest. This makes correctly
+ rendered 'disc' and 'square' list item markers appear identical,
+ while a bogus clear pixel in the middle of a 'disc' marker (the
+ present bug) still shows up.
+
+ The exact placement and rendering of the marker is up to the user
+ agent, so these values are specific to Firefox. The right and left
+ inset values fall outside the usual 0%-100% range because the
+ marker appears to the left of the <li>'s border box. */
+ clip-path: inset(44% 159% 44% -74%);
+ list-style-type: square;
+ }
+ </style>
+ </head>
+ <body>
+ <ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/1681610.html b/gfx/tests/reftest/1681610.html
new file mode 100644
index 0000000000..e93fe5bd5b
--- /dev/null
+++ b/gfx/tests/reftest/1681610.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ margin-left: 100px;
+ font-size: 500%;
+ font-family: Nimbus Sans, sans-serif;
+ }
+ ul {
+ width: 1em; /* This must to be non-zero, for the percentages to work. */
+ }
+ li {
+ /* This clip path is chosen to mask off all the rounded edges of a
+ 'disc' list item marker, to hide the anti-aliased pixels along the
+ round edges of the bullets from the reftest. This makes correctly
+ rendered 'disc' and 'square' list item markers appear identical,
+ while a bogus clear pixel in the middle of a 'disc' marker (the
+ present bug) still shows up.
+
+ The exact placement and rendering of the marker is up to the user
+ agent, so these values are specific to Firefox. The right and left
+ inset values fall outside the usual 0%-100% range because the
+ marker appears to the left of the <li>'s border box. */
+ clip-path: inset(44% 159% 44% -74%);
+ }
+ </style>
+ </head>
+ <body>
+ <ul>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ <li></li>
+ </ul>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/468496-1-ref.html b/gfx/tests/reftest/468496-1-ref.html
new file mode 100644
index 0000000000..c045d7d769
--- /dev/null
+++ b/gfx/tests/reftest/468496-1-ref.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<html><head>
+<title>stretched image artifacts (test)</title>
+<style>
+div {
+ height: 5px;
+ background-image: url("data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=");
+ background-repeat: repeat-x;
+}
+</style>
+</head><body>
+<div style="width: 540px">
+</div><div style="width: 541px">
+</div><div style="width: 542px">
+</div><div style="width: 543px">
+</div><div style="width: 544px">
+</div><div style="width: 545px">
+</div><div style="width: 546px">
+</div><div style="width: 547px">
+</div><div style="width: 548px">
+</div><div style="width: 549px">
+</div><div style="width: 550px">
+</div><div style="width: 551px">
+</div><div style="width: 552px">
+</div><div style="width: 553px">
+</div><div style="width: 554px">
+</div><div style="width: 555px">
+</div><div style="width: 556px">
+</div><div style="width: 557px">
+</div><div style="width: 558px">
+</div><div style="width: 559px">
+</div></body></html>
diff --git a/gfx/tests/reftest/468496-1.html b/gfx/tests/reftest/468496-1.html
new file mode 100644
index 0000000000..a6bc6dde63
--- /dev/null
+++ b/gfx/tests/reftest/468496-1.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html><head>
+<title>stretched image artifacts (test)</title>
+<style>
+img { display: block; }
+div { height: 5px; }
+</style>
+</head><body>
+<div style="width: 540px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 541px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 542px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 543px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 544px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+
+</div><div style="width: 545px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 546px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 547px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 548px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 549px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 550px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 551px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 552px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 553px">
+
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 554px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 555px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 556px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 557px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 558px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div><div style="width: 559px">
+<img width="100%" height="2" src="data:image/gif;base64,R0lGODdhAQACAPABAAD/AP///ywAAAAAAQACAAACAkQKADs=">
+</div></body></html>
diff --git a/gfx/tests/reftest/611498-1.html b/gfx/tests/reftest/611498-1.html
new file mode 100644
index 0000000000..28a9059ae4
--- /dev/null
+++ b/gfx/tests/reftest/611498-1.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html class="reftest-wait">
+<script type="text/javascript">
+function done()
+{
+ document.documentElement.className = "";
+}
+function move()
+{
+ elem = document.getElementById("moving");
+ elem.addEventListener("transitionend", done, true);
+ elem.style.position = "relative";
+ elem.style.top = 0;
+}
+</script>
+<body style="background: url('bwinton.jpg'); background-attachment: fixed" onload="move()">
+<div id="moving" style="position: absolute; top: 50px; background-image: url('blacktrans.png'); width: 100px; height: 100px; -moz-transition: top 0.1s; padding: 2px;">blah blah blah</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/611498-ref.html b/gfx/tests/reftest/611498-ref.html
new file mode 100644
index 0000000000..0763857c8b
--- /dev/null
+++ b/gfx/tests/reftest/611498-ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html>
+<body style="background: url('bwinton.jpg'); background-attachment: fixed">
+<div style="background: url('blacktrans.png'); width: 100px; height: 100px; padding: 2px;">blah blah blah</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/709477-1-ref.html b/gfx/tests/reftest/709477-1-ref.html
new file mode 100644
index 0000000000..eb8897f8ee
--- /dev/null
+++ b/gfx/tests/reftest/709477-1-ref.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>glyph clipping (reference)</title>
+ <style>
+ #clip { position: absolute;
+ overflow: hidden;
+ font-size: 16px;
+ width: 500px;
+ height: 300px;}
+ /* Offsets keep the text far enough away from clip boundaries so that
+ cairo knows the text is within the clip. Non-unit alpha color makes
+ the bug show even without antialiasing. */
+ #text { position: absolute;
+ left: 100px;
+ top: 100px;
+ color: rgba(0,0,0,0.4)}
+ #cover { position: absolute;
+ top: 90px;
+ left: 120px;
+ height: 50px;
+ width: 60px;
+ background: transparent; }
+ #mod { position: absolute;
+ top: 400px;
+ left: 0px;
+ height: 2000px;
+ width: 600px;
+ background: transparent; }
+ </style>
+</head>
+<body>
+ <div id="clip">
+ <div id="text">
+ Some text that was</br>
+ initially partially covered.</br>
+ </div>
+ </div>
+ <div id="cover">
+ </div>
+ <div id="mod">
+ </div>
+</body>
+<script>
+ scrollTo(0,1);
+</script>
+</html>
diff --git a/gfx/tests/reftest/709477-1.html b/gfx/tests/reftest/709477-1.html
new file mode 100644
index 0000000000..8895ea67ec
--- /dev/null
+++ b/gfx/tests/reftest/709477-1.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <title>glyph clipping (test)</title>
+ <style>
+ #clip { position: absolute;
+ overflow: hidden;
+ font-size: 16px;
+ width: 500px;
+ height: 300px;}
+ /* Offsets keep the text far enough away from clip boundaries so that
+ cairo knows the text is within the clip. Non-unit alpha color makes
+ the bug show even without antialiasing. */
+ #text { position: absolute;
+ left: 100px;
+ top: 100px;
+ color: rgba(0,0,0,0.4)}
+ #cover { position: absolute;
+ top: 90px;
+ left: 120px;
+ height: 50px;
+ width: 60px;
+ background: green; }
+ #mod { position: absolute;
+ top: 400px;
+ left: 0px;
+ height: 2000px;
+ width: 600px;
+ background: green; }
+ </style>
+ <script>
+
+function doPaint()
+{
+ window.addEventListener("MozAfterPaint", doScroll);
+ var cover = document.getElementById("cover");
+ cover.style.background = "transparent";
+ var mod = document.getElementById("mod");
+ mod.style.background = "transparent";
+}
+
+function doScroll()
+{
+ window.removeEventListener("MozAfterPaint", doScroll);
+ window.addEventListener("MozAfterPaint", endTest);
+ scrollTo(0,1);
+}
+
+function endTest()
+{
+ document.documentElement.removeAttribute("class");
+}
+
+document.addEventListener("MozReftestInvalidate", doPaint);
+ </script>
+</head>
+<body>
+ <div id="clip">
+ <div id="text">
+ Some text that was</br>
+ initially partially covered.</br>
+ </div>
+ </div>
+ <div id="cover">
+ </div>
+ <div id="mod">
+ </div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/853889-1-ref.html b/gfx/tests/reftest/853889-1-ref.html
new file mode 100644
index 0000000000..1a8513dedf
--- /dev/null
+++ b/gfx/tests/reftest/853889-1-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html>
+ <head><title>Testcase for bug 853889</title></head>
+ <body>
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="400px" height="400px">
+ <path d="M 0 0 L 0 50 L 400 50 L 400 0 Z"
+ fill="rgb(12,200,12)"></path>
+ </svg>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/853889-1.html b/gfx/tests/reftest/853889-1.html
new file mode 100644
index 0000000000..2b728c2975
--- /dev/null
+++ b/gfx/tests/reftest/853889-1.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+ <head><title>Testcase for bug 853889</title></head>
+ <body>
+ <svg version="1.1" xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink"
+ width="400px" height="400px">
+
+ <path d="M 0 400 L 0 450 L 600 450 L 600 400 Z"
+ fill="rgb(200,12,12)"></path>
+ <path d="M 0 0 L 0 50 L 600 50 L 600 0 Z"
+ fill="rgb(200,12,12)"></path>
+
+ <path d="M 0 0 L 0 50 L 600 50 L 600 0 Z
+ M 0 400 L 0 450 L 600 450 L 600 400 Z"
+ fill="rgb(12,200,12)"></path>
+ </svg>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/blacktrans.png b/gfx/tests/reftest/blacktrans.png
new file mode 100644
index 0000000000..1b3ef9baa5
--- /dev/null
+++ b/gfx/tests/reftest/blacktrans.png
Binary files differ
diff --git a/gfx/tests/reftest/bug1523410-translate-scale-snap-ref.html b/gfx/tests/reftest/bug1523410-translate-scale-snap-ref.html
new file mode 100644
index 0000000000..159d8a31e0
--- /dev/null
+++ b/gfx/tests/reftest/bug1523410-translate-scale-snap-ref.html
@@ -0,0 +1,37 @@
+<html reftest-zoom="0.8">
+<head>
+<style type="text/css">
+div.outermost {
+max-width: 1240px;
+}
+
+div.outer {
+max-width: 50%;
+}
+
+div.inner {
+transform: translateX(1px);
+position: relative;
+overflow: hidden;
+padding-bottom: 56.25%;
+}
+
+div.innermost {
+position: absolute;
+will-change: transform;
+transform: scale(1.0);
+}
+</style>
+</head>
+<body>
+<div class="outermost">
+<div class="outer">
+<div class="inner">
+<div class="innermost">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmwAAAFdCAIAAAAxOabnAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wEcFTUw5a6RmgAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAERklEQVR42u3VMQEAAAjDMMC/56EB7kRCn3YKAPgYCQDARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAcBEAQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQAwUQDARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUAEwUADBRADBRADBRADBRAMBEAcBEAcBEAcBEAcBEAQATBQATBQATBQATBQATBQBMFABMFABMFABMFAAwUQAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFAEwUAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAQATBQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAcBEAQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQAwUQDARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUAExUAgAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFAEwUAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAQATBQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAI4WK5EDuRah8ngAAAAASUVORK5CYII=">
+</div>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/bug1523410-translate-scale-snap.html b/gfx/tests/reftest/bug1523410-translate-scale-snap.html
new file mode 100644
index 0000000000..9c894c748a
--- /dev/null
+++ b/gfx/tests/reftest/bug1523410-translate-scale-snap.html
@@ -0,0 +1,37 @@
+<html reftest-zoom="0.8">
+<head>
+<style type="text/css">
+div.outermost {
+max-width: 1240px;
+}
+
+div.outer {
+max-width: 50%;
+}
+
+div.inner {
+transform: translateX(1px);
+position: relative;
+overflow: hidden;
+padding-bottom: 56.25%;
+}
+
+div.innermost {
+position: absolute;
+will-change: transform;
+transform: scale(2.0);
+}
+</style>
+</head>
+<body>
+<div class="outermost">
+<div class="outer">
+<div class="inner">
+<div class="innermost">
+<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAmwAAAFdCAIAAAAxOabnAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4wEcFTUw5a6RmgAAAB1pVFh0Q29tbWVudAAAAAAAQ3JlYXRlZCB3aXRoIEdJTVBkLmUHAAAERklEQVR42u3VMQEAAAjDMMC/56EB7kRCn3YKAPgYCQDARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAcBEAQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQAwUQDARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUAEwUADBRADBRADBRADBRAMBEAcBEAcBEAcBEAcBEAQATBQATBQATBQATBQATBQBMFABMFABMFABMFAAwUQAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFAEwUAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAQATBQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAcBEAQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQAwUQDARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAEwUAEwUAEwUAEwUAExUAgAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQAMFEAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFAEwUAEwUAEwUAEwUAEwUADBRADBRADBRADBRADBRAMBEAcBEAcBEAcBEAQATBQATBQATBQATBQATBQBMFABMFABMFABMFABMFAAwUQAwUQAwUQAwUQDARAHARAHARAHARAHARAEAEwUAEwUAEwUAEwUAEwUATBQATBQATBQATBQATBQAMFEAMFEAMFEAMFEAwEQBwEQBwEQBwEQBwEQBABMFABMFABMFABMFABMFAI4WK5EDuRah8ngAAAAASUVORK5CYII=">
+</div>
+</div>
+</div>
+</div>
+</body>
+</html>
diff --git a/gfx/tests/reftest/bwinton.jpg b/gfx/tests/reftest/bwinton.jpg
new file mode 100644
index 0000000000..708354511b
--- /dev/null
+++ b/gfx/tests/reftest/bwinton.jpg
Binary files differ
diff --git a/gfx/tests/reftest/pass.svg b/gfx/tests/reftest/pass.svg
new file mode 100644
index 0000000000..c09c6601e8
--- /dev/null
+++ b/gfx/tests/reftest/pass.svg
@@ -0,0 +1,8 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
+ <title>Testcase reference file for generic pass condition</title>
+ <rect width="100%" height="100%" fill="lime"/>
+</svg>
diff --git a/gfx/tests/reftest/picture-caching-on-async-zoom.html b/gfx/tests/reftest/picture-caching-on-async-zoom.html
new file mode 100644
index 0000000000..f32ab02d3b
--- /dev/null
+++ b/gfx/tests/reftest/picture-caching-on-async-zoom.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <meta name="viewport" content="width=device-width"/>
+ <title>The Grid in an overflowing div</title>
+ <style type="text/css">
+ html, body {
+ padding: 0;
+ border: 0;
+ margin: 0;
+ scrollbar-width: none;
+ }
+ table {
+ padding: 0;
+ margin: 0;
+ border-top: none;
+ border-left: none;
+ border-right: 1px solid black;
+ border-bottom: 1px solid black;
+ }
+ tr {
+ padding: 0;
+ border: 0;
+ margin: 0;
+ }
+ td {
+ /* top border counts as part of height, but
+ left border doesn't count as part of width.
+ go figure.
+ */
+ min-height: 99px;
+ height: 99px;
+ max-height: 99px;
+ min-width: 99px;
+ width: 99px;
+ max-width: 99px;
+ padding: 0;
+ border-left: 1px solid black;
+ border-top: 1px solid black;
+ border-right: none;
+ border-bottom: none;
+ margin: 0;
+ font-size: 12px;
+ text-align: left;
+ vertical-align: top;
+ font-family: monospace;
+ }
+ </style>
+ </head>
+ <body>
+ <div style="color: red">this text is above the scrolling div. the div below is 300x400</div>
+ <div id="nest" style="overflow: scroll; scrollbar-width: none; height: 400px; width: 300px">
+ <table cellspacing="0" cellpadding="0" border="0">
+ <script type="text/javascript">
+ var PAGE_SIZE = 2000;
+ var GRID_SIZE = 100;
+
+ var cnt = (PAGE_SIZE / GRID_SIZE);
+ for (var y = 0; y < cnt; y++) {
+ document.writeln( "<tr>" );
+ for (var x = 0; x < cnt; x++) {
+ var color = ((x + y) % 2) ? "blue" : "red";
+ document.writeln( "<td style='background-color: " + color + "'></td>" );
+ }
+ document.writeln( "</tr>" );
+ }
+ </script>
+ </table>
+ </div>
+ <div style="color: red">this text is below the scrolling div</div>
+ <script>
+ if (location.search == "?ref") {
+ // In the reference case we use a CSS transform so that we don't use
+ // the async-zoom codepath (which is handled differently by WR).
+ document.documentElement.setAttribute("style", "transform: scale(1.1); transform-origin: top left");
+ document.documentElement.classList.remove("reftest-wait");
+ } else {
+ // In the test case, we want to first paint the unscaled content, so that
+ // WR populates the picture cache. Then we apply an async zoom and paint
+ // again for the final snapshot. The bug in this case was that WR wasn't
+ // properly invalidating the picture cache tiles and so things would
+ // appear incorrectly.
+ window.addEventListener("MozAfterPaint", () => {
+ document.documentElement.setAttribute("reftest-async-zoom", "1.1");
+ document.documentElement.classList.remove("reftest-wait");
+ });
+ }
+ </script>
+ </body>
+</html>
diff --git a/gfx/tests/reftest/reftest.list b/gfx/tests/reftest/reftest.list
new file mode 100644
index 0000000000..4505a13709
--- /dev/null
+++ b/gfx/tests/reftest/reftest.list
@@ -0,0 +1,29 @@
+# 468496-1 will also detect bugs in video drivers.
+== 468496-1.html 468496-1-ref.html
+fuzzy(0-175,0-443) random-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 611498-1.html 611498-ref.html # Bug 1392106
+skip-if((gtkWidget||Android)&&webrender) == 709477-1.html 709477-1-ref.html #Bug 1620096
+skip-if(!asyncPan) == 1086723.html 1086723-ref.html
+== 853889-1.html 853889-1-ref.html
+skip-if(Android) fuzzy-if(skiaContent,0-1,0-587) == 1143303-1.svg pass.svg
+fuzzy(0-100,0-30) == 1149923.html 1149923-ref.html # use fuzzy due to few distorted pixels caused by border-radius
+== 1131264-1.svg pass.svg
+== 1419528.html 1419528-ref.html
+== 1424673.html 1424673-ref.html
+== 1429411.html 1429411-ref.html
+fuzzy-if(winWidget,0-1,0-4) == 1435143.html 1435143-ref.html
+== 1444904.html 1444904-ref.html
+fuzzy-if(winWidget&&webrender,90-95,1000-1100) == 1451168.html 1451168-ref.html
+== 1461313.html 1461313-ref.html
+fuzzy(5-32,21908-26621) fuzzy-if(webrender,4-5,868-1039) == 1463802.html 1463802-ref.html
+fuzzy(0-11,0-4) == 1474722.html 1474722-ref.html
+== 1501195.html 1501195-ref.html
+== 1519754.html 1519754-ref.html
+skip-if(!asyncPan) == 1524261.html 1524261-ref.html
+fuzzy-if(webrender,14-14,44-95) == 1524353.html 1524353-ref.html
+fuzzy-if(webrender,2-7,17500-36908) == 1523776.html 1523776-ref.html
+== bug1523410-translate-scale-snap.html bug1523410-translate-scale-snap-ref.html
+== 1523080.html 1523080-ref.html
+== 1616444-same-color-different-paths.html 1616444-same-color-different-paths-ref.html
+skip-if(!asyncPan||!webrender||Android) fuzzy-if(winWidget,94-94,3415-3415) pref(apz.allow_zooming,true) == picture-caching-on-async-zoom.html picture-caching-on-async-zoom.html?ref
+pref(apz.allow_zooming,true) == 1662062-1-no-blurry.html 1662062-1-ref.html
+== 1681610.html 1681610-ref.html