summaryrefslogtreecommitdiffstats
path: root/layout/base/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 01:47:29 +0000
commit0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d (patch)
treea31f07c9bcca9d56ce61e9a1ffd30ef350d513aa /layout/base/tests
parentInitial commit. (diff)
downloadfirefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.tar.xz
firefox-esr-0ebf5bdf043a27fd3dfb7f92e0cb63d88954c44d.zip
Adding upstream version 115.8.0esr.upstream/115.8.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'layout/base/tests')
-rw-r--r--layout/base/tests/Ahem.ttfbin0 -> 12480 bytes
-rw-r--r--layout/base/tests/accessiblecaret_magnifier.html91
-rw-r--r--layout/base/tests/border_radius_hit_testing_iframe.html27
-rw-r--r--layout/base/tests/browser.ini36
-rw-r--r--layout/base/tests/browser_bug1701027-1.js130
-rw-r--r--layout/base/tests/browser_bug1701027-2.js126
-rw-r--r--layout/base/tests/browser_bug1757410.js62
-rw-r--r--layout/base/tests/browser_bug1787079.js86
-rw-r--r--layout/base/tests/browser_bug1791083.js81
-rw-r--r--layout/base/tests/browser_bug617076.js71
-rw-r--r--layout/base/tests/browser_bug839103.js82
-rw-r--r--layout/base/tests/browser_disableDialogs_onbeforeunload.js64
-rw-r--r--layout/base/tests/browser_onbeforeunload_only_after_interaction.js75
-rw-r--r--layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js96
-rw-r--r--layout/base/tests/browser_scroll_into_view_in_out_of_process_iframe.js50
-rw-r--r--layout/base/tests/browser_select_popup_position_in_out_of_process_iframe.js122
-rw-r--r--layout/base/tests/browser_visual_viewport_iframe.js59
-rw-r--r--layout/base/tests/bug1007065-1-ref.html15
-rw-r--r--layout/base/tests/bug1007065-1.html15
-rw-r--r--layout/base/tests/bug1007067-1-ref.html20
-rw-r--r--layout/base/tests/bug1007067-1.html20
-rw-r--r--layout/base/tests/bug1061468-ref.html13
-rw-r--r--layout/base/tests/bug1061468.html40
-rw-r--r--layout/base/tests/bug106855-1-ref.html27
-rw-r--r--layout/base/tests/bug106855-1.html25
-rw-r--r--layout/base/tests/bug106855-2.html26
-rw-r--r--layout/base/tests/bug1078327_inner.html107
-rw-r--r--layout/base/tests/bug1080360_inner.html83
-rw-r--r--layout/base/tests/bug1080361_inner.html114
-rw-r--r--layout/base/tests/bug1082486-1-ref.html18
-rw-r--r--layout/base/tests/bug1082486-1.html27
-rw-r--r--layout/base/tests/bug1082486-2-ref.html12
-rw-r--r--layout/base/tests/bug1082486-2.html12
-rw-r--r--layout/base/tests/bug1093686_inner.html83
-rw-r--r--layout/base/tests/bug1097242-1-ref.html14
-rw-r--r--layout/base/tests/bug1097242-1.html22
-rw-r--r--layout/base/tests/bug1109968-1-ref.html17
-rw-r--r--layout/base/tests/bug1109968-1.html23
-rw-r--r--layout/base/tests/bug1109968-2-ref.html17
-rw-r--r--layout/base/tests/bug1109968-2.html23
-rw-r--r--layout/base/tests/bug1123067-1.html38
-rw-r--r--layout/base/tests/bug1123067-2.html34
-rw-r--r--layout/base/tests/bug1123067-3.html35
-rw-r--r--layout/base/tests/bug1123067-ref.html33
-rw-r--r--layout/base/tests/bug1132768-1-ref.html12
-rw-r--r--layout/base/tests/bug1132768-1.html17
-rw-r--r--layout/base/tests/bug1153130_inner.html72
-rw-r--r--layout/base/tests/bug1162990_inner_1.html146
-rw-r--r--layout/base/tests/bug1162990_inner_2.html147
-rw-r--r--layout/base/tests/bug1226904.html35
-rw-r--r--layout/base/tests/bug1237236-1-ref.html30
-rw-r--r--layout/base/tests/bug1237236-1.html31
-rw-r--r--layout/base/tests/bug1237236-2-ref.html29
-rw-r--r--layout/base/tests/bug1237236-2.html30
-rw-r--r--layout/base/tests/bug1258308-1-ref.html34
-rw-r--r--layout/base/tests/bug1258308-1.html42
-rw-r--r--layout/base/tests/bug1258308-2-ref.html30
-rw-r--r--layout/base/tests/bug1258308-2.html31
-rw-r--r--layout/base/tests/bug1259949-1-ref.html30
-rw-r--r--layout/base/tests/bug1259949-1.html34
-rw-r--r--layout/base/tests/bug1259949-2-ref.html30
-rw-r--r--layout/base/tests/bug1259949-2.html30
-rw-r--r--layout/base/tests/bug1263288-ref.html28
-rw-r--r--layout/base/tests/bug1263288.html30
-rw-r--r--layout/base/tests/bug1263357-1-ref.html28
-rw-r--r--layout/base/tests/bug1263357-1.html34
-rw-r--r--layout/base/tests/bug1263357-2-ref.html28
-rw-r--r--layout/base/tests/bug1263357-2.html34
-rw-r--r--layout/base/tests/bug1263357-3-ref.html27
-rw-r--r--layout/base/tests/bug1263357-3.html28
-rw-r--r--layout/base/tests/bug1263357-4-ref.html27
-rw-r--r--layout/base/tests/bug1263357-4.html28
-rw-r--r--layout/base/tests/bug1263357-5-ref.html27
-rw-r--r--layout/base/tests/bug1263357-5.html28
-rw-r--r--layout/base/tests/bug1354478-1-ref.html35
-rw-r--r--layout/base/tests/bug1354478-1.html35
-rw-r--r--layout/base/tests/bug1354478-2-ref.html35
-rw-r--r--layout/base/tests/bug1354478-2.html35
-rw-r--r--layout/base/tests/bug1354478-3-ref.html38
-rw-r--r--layout/base/tests/bug1354478-3.html38
-rw-r--r--layout/base/tests/bug1354478-4-ref.html38
-rw-r--r--layout/base/tests/bug1354478-4.html38
-rw-r--r--layout/base/tests/bug1354478-5-ref.html38
-rw-r--r--layout/base/tests/bug1354478-5.html38
-rw-r--r--layout/base/tests/bug1354478-6-ref.html38
-rw-r--r--layout/base/tests/bug1354478-6.html38
-rw-r--r--layout/base/tests/bug1359411-ref.html11
-rw-r--r--layout/base/tests/bug1359411.html12
-rw-r--r--layout/base/tests/bug1415416-ref.html21
-rw-r--r--layout/base/tests/bug1415416.html28
-rw-r--r--layout/base/tests/bug1423331-1-ref.html23
-rw-r--r--layout/base/tests/bug1423331-1.html28
-rw-r--r--layout/base/tests/bug1423331-2-ref.html23
-rw-r--r--layout/base/tests/bug1423331-2.html29
-rw-r--r--layout/base/tests/bug1423331-3.html26
-rw-r--r--layout/base/tests/bug1423331-4.html27
-rw-r--r--layout/base/tests/bug1448730.html103
-rw-r--r--layout/base/tests/bug1484094-1-ref.html21
-rw-r--r--layout/base/tests/bug1484094-1.html23
-rw-r--r--layout/base/tests/bug1484094-2-ref.html21
-rw-r--r--layout/base/tests/bug1484094-2.html23
-rw-r--r--layout/base/tests/bug1496118-ref.html25
-rw-r--r--layout/base/tests/bug1496118.html37
-rw-r--r--layout/base/tests/bug1506547-1.html29
-rw-r--r--layout/base/tests/bug1506547-2.html30
-rw-r--r--layout/base/tests/bug1506547-3.html32
-rw-r--r--layout/base/tests/bug1506547-4-ref.html21
-rw-r--r--layout/base/tests/bug1506547-4.html21
-rw-r--r--layout/base/tests/bug1506547-5-ref.html26
-rw-r--r--layout/base/tests/bug1506547-5.html27
-rw-r--r--layout/base/tests/bug1506547-6.html27
-rw-r--r--layout/base/tests/bug1510942-1-ref.html20
-rw-r--r--layout/base/tests/bug1510942-1.html21
-rw-r--r--layout/base/tests/bug1510942-2-ref.html4
-rw-r--r--layout/base/tests/bug1510942-2.html21
-rw-r--r--layout/base/tests/bug1516963-1-ref.html16
-rw-r--r--layout/base/tests/bug1516963-1.html30
-rw-r--r--layout/base/tests/bug1516963-2-ref.html16
-rw-r--r--layout/base/tests/bug1516963-2.html30
-rw-r--r--layout/base/tests/bug1516963-3-ref.html20
-rw-r--r--layout/base/tests/bug1516963-3.html34
-rw-r--r--layout/base/tests/bug1516963-4-ref.html20
-rw-r--r--layout/base/tests/bug1516963-4.html34
-rw-r--r--layout/base/tests/bug1516963-5-ref.html16
-rw-r--r--layout/base/tests/bug1516963-5.html30
-rw-r--r--layout/base/tests/bug1516963-6-ref.html20
-rw-r--r--layout/base/tests/bug1516963-6.html34
-rw-r--r--layout/base/tests/bug1518339-1-ref.html16
-rw-r--r--layout/base/tests/bug1518339-1.html23
-rw-r--r--layout/base/tests/bug1518339-2-ref.html25
-rw-r--r--layout/base/tests/bug1518339-2.html23
-rw-r--r--layout/base/tests/bug1524266-1-ref.html30
-rw-r--r--layout/base/tests/bug1524266-1.html27
-rw-r--r--layout/base/tests/bug1524266-2-ref.html24
-rw-r--r--layout/base/tests/bug1524266-2.html38
-rw-r--r--layout/base/tests/bug1524266-3.html37
-rw-r--r--layout/base/tests/bug1524266-4.html30
-rw-r--r--layout/base/tests/bug1529492-1-ref.html30
-rw-r--r--layout/base/tests/bug1529492-1.html35
-rw-r--r--layout/base/tests/bug1550869-1-ref.html19
-rw-r--r--layout/base/tests/bug1550869-1a.html33
-rw-r--r--layout/base/tests/bug1550869-1b.html33
-rw-r--r--layout/base/tests/bug1550869-1c.html33
-rw-r--r--layout/base/tests/bug1550869-2-ref.html15
-rw-r--r--layout/base/tests/bug1550869-2a.html30
-rw-r--r--layout/base/tests/bug1550869-2b.html30
-rw-r--r--layout/base/tests/bug1550869-2c.html30
-rw-r--r--layout/base/tests/bug1550869-2d.html30
-rw-r--r--layout/base/tests/bug1591282-1-ref.html23
-rw-r--r--layout/base/tests/bug1591282-1.html27
-rw-r--r--layout/base/tests/bug1611661-ref.html18
-rw-r--r--layout/base/tests/bug1611661.html23
-rw-r--r--layout/base/tests/bug1634543-1-ref.html20
-rw-r--r--layout/base/tests/bug1634543-1.html25
-rw-r--r--layout/base/tests/bug1634543-2.html24
-rw-r--r--layout/base/tests/bug1634543-3.html20
-rw-r--r--layout/base/tests/bug1634543-4.html23
-rw-r--r--layout/base/tests/bug1634743-1-ref.html23
-rw-r--r--layout/base/tests/bug1634743-1.html29
-rw-r--r--layout/base/tests/bug1637476-1-ref.html24
-rw-r--r--layout/base/tests/bug1637476-1.html24
-rw-r--r--layout/base/tests/bug1637476-2-ref.html24
-rw-r--r--layout/base/tests/bug1637476-2.html24
-rw-r--r--layout/base/tests/bug1637476-3-ref.html24
-rw-r--r--layout/base/tests/bug1637476-3.html24
-rw-r--r--layout/base/tests/bug1663475-1-ref.html28
-rw-r--r--layout/base/tests/bug1663475-1.html32
-rw-r--r--layout/base/tests/bug1663475-2-ref.html29
-rw-r--r--layout/base/tests/bug1663475-2.html33
-rw-r--r--layout/base/tests/bug1670531-1.html27
-rw-r--r--layout/base/tests/bug1670531-2.html27
-rw-r--r--layout/base/tests/bug1670531-3-ref.html27
-rw-r--r--layout/base/tests/bug1670531-3.html27
-rw-r--r--layout/base/tests/bug1670531-4.html27
-rw-r--r--layout/base/tests/bug240933-1-ref.html12
-rw-r--r--layout/base/tests/bug240933-1.html13
-rw-r--r--layout/base/tests/bug240933-2.html15
-rw-r--r--layout/base/tests/bug369950-subframe.xml11
-rw-r--r--layout/base/tests/bug389321-1-ref.html17
-rw-r--r--layout/base/tests/bug389321-1.html19
-rw-r--r--layout/base/tests/bug389321-2-ref.html9
-rw-r--r--layout/base/tests/bug389321-2.html9
-rw-r--r--layout/base/tests/bug389321-3-ref.html9
-rw-r--r--layout/base/tests/bug389321-3.html9
-rw-r--r--layout/base/tests/bug450930.xhtml181
-rw-r--r--layout/base/tests/bug482484-ref.html18
-rw-r--r--layout/base/tests/bug482484.html22
-rw-r--r--layout/base/tests/bug503399-ref.html42
-rw-r--r--layout/base/tests/bug503399.html41
-rw-r--r--layout/base/tests/bug512295-1-ref.html30
-rw-r--r--layout/base/tests/bug512295-1.html36
-rw-r--r--layout/base/tests/bug512295-2-ref.html30
-rw-r--r--layout/base/tests/bug512295-2.html36
-rw-r--r--layout/base/tests/bug558663.html103
-rw-r--r--layout/base/tests/bug583889_inner1.html64
-rw-r--r--layout/base/tests/bug583889_inner2.html5
-rw-r--r--layout/base/tests/bug585922-ref.html21
-rw-r--r--layout/base/tests/bug585922.html35
-rw-r--r--layout/base/tests/bug597519-1-ref.html12
-rw-r--r--layout/base/tests/bug597519-1.html15
-rw-r--r--layout/base/tests/bug602141-1-ref.html18
-rw-r--r--layout/base/tests/bug602141-1.html21
-rw-r--r--layout/base/tests/bug602141-2-ref.html18
-rw-r--r--layout/base/tests/bug602141-2.html23
-rw-r--r--layout/base/tests/bug602141-3-ref.html18
-rw-r--r--layout/base/tests/bug602141-3.html21
-rw-r--r--layout/base/tests/bug602141-4-ref.html18
-rw-r--r--layout/base/tests/bug602141-4.html21
-rw-r--r--layout/base/tests/bug612271-1.html15
-rw-r--r--layout/base/tests/bug612271-2.html15
-rw-r--r--layout/base/tests/bug612271-3.html15
-rw-r--r--layout/base/tests/bug612271-ref.html17
-rw-r--r--layout/base/tests/bug613433-1.html24
-rw-r--r--layout/base/tests/bug613433-2.html24
-rw-r--r--layout/base/tests/bug613433-3.html24
-rw-r--r--layout/base/tests/bug613433-ref.html21
-rw-r--r--layout/base/tests/bug613807-1-ref.html6
-rw-r--r--layout/base/tests/bug613807-1.html90
-rw-r--r--layout/base/tests/bug632215-1.html29
-rw-r--r--layout/base/tests/bug632215-2.html28
-rw-r--r--layout/base/tests/bug632215-ref.html17
-rw-r--r--layout/base/tests/bug633044-1-ref.html16
-rw-r--r--layout/base/tests/bug633044-1.html24
-rw-r--r--layout/base/tests/bug634406-1-ref.html10
-rw-r--r--layout/base/tests/bug634406-1.html16
-rw-r--r--layout/base/tests/bug644428-1-ref.html17
-rw-r--r--layout/base/tests/bug644428-1.html19
-rw-r--r--layout/base/tests/bug646382-1-ref.html21
-rw-r--r--layout/base/tests/bug646382-1.html22
-rw-r--r--layout/base/tests/bug646382-2-ref.html18
-rw-r--r--layout/base/tests/bug646382-2.html21
-rw-r--r--layout/base/tests/bug664087-1-ref.html21
-rw-r--r--layout/base/tests/bug664087-1.html25
-rw-r--r--layout/base/tests/bug664087-2-ref.html21
-rw-r--r--layout/base/tests/bug664087-2.html25
-rw-r--r--layout/base/tests/bug682712-1-ref.html24
-rw-r--r--layout/base/tests/bug682712-1.html32
-rw-r--r--layout/base/tests/bug687297_a.html17
-rw-r--r--layout/base/tests/bug687297_b.html17
-rw-r--r--layout/base/tests/bug687297_c.html17
-rw-r--r--layout/base/tests/bug746993-1-ref.html20
-rw-r--r--layout/base/tests/bug746993-1.html22
-rw-r--r--layout/base/tests/bug839103.css1
-rw-r--r--layout/base/tests/bug851445_helper.html11
-rw-r--r--layout/base/tests/bug923376-ref.html12
-rw-r--r--layout/base/tests/bug923376.html16
-rw-r--r--layout/base/tests/bug956530-1-ref.html29
-rw-r--r--layout/base/tests/bug956530-1.html35
-rw-r--r--layout/base/tests/bug966992-1-ref.html40
-rw-r--r--layout/base/tests/bug966992-1.html36
-rw-r--r--layout/base/tests/bug966992-2-ref.html42
-rw-r--r--layout/base/tests/bug966992-2.html38
-rw-r--r--layout/base/tests/bug966992-3-ref.html26
-rw-r--r--layout/base/tests/bug966992-3.html26
-rw-r--r--layout/base/tests/bug970964_inner.html357
-rw-r--r--layout/base/tests/bug970964_inner2.html356
-rw-r--r--layout/base/tests/bug977003_inner_1.html100
-rw-r--r--layout/base/tests/bug977003_inner_2.html75
-rw-r--r--layout/base/tests/bug977003_inner_3.html95
-rw-r--r--layout/base/tests/bug977003_inner_4.html100
-rw-r--r--layout/base/tests/bug977003_inner_5.html121
-rw-r--r--layout/base/tests/bug977003_inner_6.html105
-rw-r--r--layout/base/tests/bug989012-1-ref.html21
-rw-r--r--layout/base/tests/bug989012-1.html24
-rw-r--r--layout/base/tests/bug989012-2-ref.html26
-rw-r--r--layout/base/tests/bug989012-2.html29
-rw-r--r--layout/base/tests/bug989012-3-ref.html28
-rw-r--r--layout/base/tests/bug989012-3.html31
-rw-r--r--layout/base/tests/chrome/animated.gifbin0 -> 527 bytes
-rw-r--r--layout/base/tests/chrome/blue-32x32.pngbin0 -> 110 bytes
-rw-r--r--layout/base/tests/chrome/bug1041200_frame.html2
-rw-r--r--layout/base/tests/chrome/bug1041200_window.html40
-rw-r--r--layout/base/tests/chrome/bug1722890.html18
-rw-r--r--layout/base/tests/chrome/bug1722890_ref.html19
-rw-r--r--layout/base/tests/chrome/bug1769161_1.html17
-rw-r--r--layout/base/tests/chrome/bug1769161_1_ref.html17
-rw-r--r--layout/base/tests/chrome/bug1769161_2.html17
-rw-r--r--layout/base/tests/chrome/bug1769161_2_ref.html17
-rw-r--r--layout/base/tests/chrome/bug1769161_3.html17
-rw-r--r--layout/base/tests/chrome/bug1769161_3_ref.html18
-rw-r--r--layout/base/tests/chrome/bug1769161_4.html17
-rw-r--r--layout/base/tests/chrome/bug1769161_4_ref.html18
-rw-r--r--layout/base/tests/chrome/bug495648.rdf214
-rw-r--r--layout/base/tests/chrome/bug551434_childframe.html4
-rw-r--r--layout/base/tests/chrome/chrome.ini138
-rw-r--r--layout/base/tests/chrome/chrome_content_integration_window.xhtml45
-rw-r--r--layout/base/tests/chrome/color_adjust.html9
-rw-r--r--layout/base/tests/chrome/color_adjust_ref.html8
-rw-r--r--layout/base/tests/chrome/default_background_window.xhtml67
-rw-r--r--layout/base/tests/chrome/dialog_with_positioning_window.xhtml30
-rw-r--r--layout/base/tests/chrome/file_bug1018265.xhtml50
-rw-r--r--layout/base/tests/chrome/file_bug458898.html1
-rw-r--r--layout/base/tests/chrome/file_bug465448.html1
-rw-r--r--layout/base/tests/chrome/frame_css_visibility_propagation.html1
-rw-r--r--layout/base/tests/chrome/green.pngbin0 -> 91 bytes
-rw-r--r--layout/base/tests/chrome/markA.ttfbin0 -> 1568 bytes
-rw-r--r--layout/base/tests/chrome/markB.ttfbin0 -> 1568 bytes
-rw-r--r--layout/base/tests/chrome/print_page_size1.html31
-rw-r--r--layout/base/tests/chrome/print_page_size1_ref.html31
-rw-r--r--layout/base/tests/chrome/print_page_size2.html31
-rw-r--r--layout/base/tests/chrome/print_page_size2_ref.html34
-rw-r--r--layout/base/tests/chrome/print_page_size3.html31
-rw-r--r--layout/base/tests/chrome/print_page_size3_ref.html33
-rw-r--r--layout/base/tests/chrome/print_page_size4.html31
-rw-r--r--layout/base/tests/chrome/print_page_size4_ref.html31
-rw-r--r--layout/base/tests/chrome/printpreview_bug1713404_ref.html27
-rw-r--r--layout/base/tests/chrome/printpreview_bug1730091_ref.html27
-rw-r--r--layout/base/tests/chrome/printpreview_bug396024_helper.xhtml112
-rw-r--r--layout/base/tests/chrome/printpreview_bug482976_helper.xhtml68
-rw-r--r--layout/base/tests/chrome/printpreview_downloadable_font.html24
-rw-r--r--layout/base/tests/chrome/printpreview_downloadable_font_in_iframe.html25
-rw-r--r--layout/base/tests/chrome/printpreview_downloadable_font_in_iframe_ref.html24
-rw-r--r--layout/base/tests/chrome/printpreview_downloadable_font_ref.html23
-rw-r--r--layout/base/tests/chrome/printpreview_font_api.html25
-rw-r--r--layout/base/tests/chrome/printpreview_font_api_ref.html23
-rw-r--r--layout/base/tests/chrome/printpreview_font_mozprintcallback.html27
-rw-r--r--layout/base/tests/chrome/printpreview_font_mozprintcallback_ref.html28
-rw-r--r--layout/base/tests/chrome/printpreview_helper.xhtml1721
-rw-r--r--layout/base/tests/chrome/printpreview_image_select.html7
-rw-r--r--layout/base/tests/chrome/printpreview_image_select_ref.html4
-rw-r--r--layout/base/tests/chrome/printpreview_images.html25
-rw-r--r--layout/base/tests/chrome/printpreview_images_ref.html15
-rw-r--r--layout/base/tests/chrome/printpreview_images_sw.html46
-rw-r--r--layout/base/tests/chrome/printpreview_images_sw.js11
-rw-r--r--layout/base/tests/chrome/printpreview_images_sw_ref.html14
-rw-r--r--layout/base/tests/chrome/printpreview_mask.html16
-rw-r--r--layout/base/tests/chrome/printpreview_mixed_page_size_001.html11
-rw-r--r--layout/base/tests/chrome/printpreview_mixed_page_size_002.html15
-rw-r--r--layout/base/tests/chrome/printpreview_pps16.html43
-rw-r--r--layout/base/tests/chrome/printpreview_pps16_ref.html49
-rw-r--r--layout/base/tests/chrome/printpreview_pps2.html15
-rw-r--r--layout/base/tests/chrome/printpreview_pps2_ref.html40
-rw-r--r--layout/base/tests/chrome/printpreview_pps4.html19
-rw-r--r--layout/base/tests/chrome/printpreview_pps4_ref.html25
-rw-r--r--layout/base/tests/chrome/printpreview_pps6.html40
-rw-r--r--layout/base/tests/chrome/printpreview_pps6_ref.html90
-rw-r--r--layout/base/tests/chrome/printpreview_pps9.html29
-rw-r--r--layout/base/tests/chrome/printpreview_pps9_ref.html40
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw2.html16
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw2_no_margin_ref.html36
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw2_ref.html49
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw4.html19
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw4_ref.html70
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw9.html29
-rw-r--r--layout/base/tests/chrome/printpreview_pps_uw9_ref.html82
-rw-r--r--layout/base/tests/chrome/printpreview_prettyprint.xml1
-rw-r--r--layout/base/tests/chrome/printpreview_prettyprint_ref.xhtml3
-rw-r--r--layout/base/tests/chrome/printpreview_quirks.html8
-rw-r--r--layout/base/tests/chrome/printpreview_quirks_ref.html5
-rw-r--r--layout/base/tests/chrome/red.pngbin0 -> 510 bytes
-rw-r--r--layout/base/tests/chrome/test_bug1018265.xhtml37
-rw-r--r--layout/base/tests/chrome/test_bug1041200.xhtml22
-rw-r--r--layout/base/tests/chrome/test_bug396367-1.html40
-rw-r--r--layout/base/tests/chrome/test_bug396367-2.html47
-rw-r--r--layout/base/tests/chrome/test_bug420499.xhtml126
-rw-r--r--layout/base/tests/chrome/test_bug458898.html39
-rw-r--r--layout/base/tests/chrome/test_bug465448.xhtml45
-rw-r--r--layout/base/tests/chrome/test_bug514660.xhtml35
-rw-r--r--layout/base/tests/chrome/test_bug533845.xhtml49
-rw-r--r--layout/base/tests/chrome/test_bug551434.html95
-rw-r--r--layout/base/tests/chrome/test_bug708062.html43
-rw-r--r--layout/base/tests/chrome/test_bug812817.xhtml37
-rw-r--r--layout/base/tests/chrome/test_chrome_content_integration.xhtml24
-rw-r--r--layout/base/tests/chrome/test_color_scheme_browser.xhtml114
-rw-r--r--layout/base/tests/chrome/test_css_visibility_propagation.xhtml209
-rw-r--r--layout/base/tests/chrome/test_default_background.xhtml22
-rw-r--r--layout/base/tests/chrome/test_dialog_with_positioning.html20
-rw-r--r--layout/base/tests/chrome/test_document_adopted_styles.html8
-rw-r--r--layout/base/tests/chrome/test_document_adopted_styles_ref.html6
-rw-r--r--layout/base/tests/chrome/test_fixed_bg_scrolling_repaints.html40
-rw-r--r--layout/base/tests/chrome/test_getClientRectsAndTexts.html80
-rw-r--r--layout/base/tests/chrome/test_get_printer_basic_attributes.html36
-rw-r--r--layout/base/tests/chrome/test_get_printer_orientation.html50
-rw-r--r--layout/base/tests/chrome/test_get_printer_paper_sizes.html69
-rw-r--r--layout/base/tests/chrome/test_prerendered_transforms.html46
-rw-r--r--layout/base/tests/chrome/test_printer_default_settings.html63
-rw-r--r--layout/base/tests/chrome/test_printpreview.xhtml16
-rw-r--r--layout/base/tests/chrome/test_printpreview_bug396024.xhtml21
-rw-r--r--layout/base/tests/chrome/test_printpreview_bug482976.xhtml21
-rw-r--r--layout/base/tests/chrome/test_scrolling_repaints.html48
-rw-r--r--layout/base/tests/chrome/test_shadow_root_adopted_styles.html11
-rw-r--r--layout/base/tests/chrome/test_shadow_root_adopted_styles_ref.html11
-rw-r--r--layout/base/tests/chrome/test_shared_adopted_styles.html19
-rw-r--r--layout/base/tests/chrome/test_shared_adopted_styles_ref.html16
-rw-r--r--layout/base/tests/chrome/test_will_change.html140
-rw-r--r--layout/base/tests/chrome/window_css_visibility_propagation-1.xhtml6
-rw-r--r--layout/base/tests/chrome/window_css_visibility_propagation-2.xhtml6
-rw-r--r--layout/base/tests/chrome/window_css_visibility_propagation-3.html3
-rw-r--r--layout/base/tests/chrome/window_css_visibility_propagation-4.html3
-rw-r--r--layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input-ref.html27
-rw-r--r--layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input.html33
-rw-r--r--layout/base/tests/file_bug607529-1.html12
-rw-r--r--layout/base/tests/file_bug607529.html51
-rw-r--r--layout/base/tests/file_bug839103.html10
-rw-r--r--layout/base/tests/file_bug842853-frame.html6
-rw-r--r--layout/base/tests/file_bug842853.html17
-rw-r--r--layout/base/tests/file_bug842853.sjs16
-rw-r--r--layout/base/tests/file_dynamic_toolbar_max_height.html56
-rw-r--r--layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame1.html3
-rw-r--r--layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame2.html1
-rw-r--r--layout/base/tests/file_lazyload_telemetry.html9
-rw-r--r--layout/base/tests/file_synthmousemove.html49
-rw-r--r--layout/base/tests/file_zoom_restore_bfcache.html92
-rw-r--r--layout/base/tests/helper_bug1701027-1.html10
-rw-r--r--layout/base/tests/helper_bug1701027-2.html10
-rw-r--r--layout/base/tests/helper_synthmousemove.html3
-rw-r--r--layout/base/tests/image_rgrg-256x256.pngbin0 -> 131 bytes
-rw-r--r--layout/base/tests/input-invalid-ref.html7
-rw-r--r--layout/base/tests/input-maxlength-invalid-change.html25
-rw-r--r--layout/base/tests/input-maxlength-ui-invalid-change.html25
-rw-r--r--layout/base/tests/input-maxlength-ui-valid-change.html28
-rw-r--r--layout/base/tests/input-maxlength-valid-before-change.html15
-rw-r--r--layout/base/tests/input-maxlength-valid-change.html28
-rw-r--r--layout/base/tests/input-minlength-invalid-change.html25
-rw-r--r--layout/base/tests/input-minlength-ui-invalid-change.html25
-rw-r--r--layout/base/tests/input-minlength-ui-valid-change.html28
-rw-r--r--layout/base/tests/input-minlength-valid-before-change.html15
-rw-r--r--layout/base/tests/input-minlength-valid-change.html28
-rw-r--r--layout/base/tests/input-password-RTL-input-ref.html23
-rw-r--r--layout/base/tests/input-password-RTL-input.html31
-rw-r--r--layout/base/tests/input-password-remask-ref.html20
-rw-r--r--layout/base/tests/input-password-remask.html23
-rw-r--r--layout/base/tests/input-password-unmask-around-emoji-ref.html40
-rw-r--r--layout/base/tests/input-password-unmask-around-emoji.html21
-rw-r--r--layout/base/tests/input-password-unmask-ref.html33
-rw-r--r--layout/base/tests/input-password-unmask.html25
-rw-r--r--layout/base/tests/input-stoppropagation-ref.html16
-rw-r--r--layout/base/tests/input-stoppropagation.html20
-rw-r--r--layout/base/tests/input-ui-valid-ref.html6
-rw-r--r--layout/base/tests/input-valid-ref.html7
-rw-r--r--layout/base/tests/interlinePosition-after-Selection-addRange-ref.html20
-rw-r--r--layout/base/tests/interlinePosition-after-Selection-addRange.html21
-rw-r--r--layout/base/tests/marionette/manifest.ini6
-rw-r--r--layout/base/tests/marionette/selection.py335
-rw-r--r--layout/base/tests/marionette/test_accessiblecaret_cursor_mode.py285
-rw-r--r--layout/base/tests/marionette/test_accessiblecaret_selection_mode.py767
-rw-r--r--layout/base/tests/mochitest.ini470
-rw-r--r--layout/base/tests/multi-range-script-select-ref.html173
-rw-r--r--layout/base/tests/multi-range-script-select.html185
-rw-r--r--layout/base/tests/multi-range-user-select-ref.html166
-rw-r--r--layout/base/tests/multi-range-user-select.html223
-rw-r--r--layout/base/tests/partial.pngbin0 -> 4000 bytes
-rw-r--r--layout/base/tests/preserve3d_sorting_hit_testing2_iframe.html96
-rw-r--r--layout/base/tests/preserve3d_sorting_hit_testing_iframe.html32
-rw-r--r--layout/base/tests/resize_flush_iframe.html17
-rw-r--r--layout/base/tests/scroll_into_view_in_child.html8
-rw-r--r--layout/base/tests/scroll_selection_into_view_window.html59
-rw-r--r--layout/base/tests/scroll_selection_into_view_window_frame.html6
-rw-r--r--layout/base/tests/selection-utils.js167
-rw-r--r--layout/base/tests/sendimagenevercomplete.sjs29
-rw-r--r--layout/base/tests/test_accessiblecaret_magnifier.html33
-rw-r--r--layout/base/tests/test_after_paint_pref.html123
-rw-r--r--layout/base/tests/test_border_radius_hit_testing.html106
-rw-r--r--layout/base/tests/test_bug1078327.html30
-rw-r--r--layout/base/tests/test_bug1080360.html30
-rw-r--r--layout/base/tests/test_bug1080361.html31
-rw-r--r--layout/base/tests/test_bug1093686.html41
-rw-r--r--layout/base/tests/test_bug1120705.html98
-rw-r--r--layout/base/tests/test_bug114649.html78
-rw-r--r--layout/base/tests/test_bug1153130.html30
-rw-r--r--layout/base/tests/test_bug1162990.html32
-rw-r--r--layout/base/tests/test_bug1216483.html204
-rw-r--r--layout/base/tests/test_bug1226904.html44
-rw-r--r--layout/base/tests/test_bug1246622.html45
-rw-r--r--layout/base/tests/test_bug1278021.html45
-rw-r--r--layout/base/tests/test_bug1448730.html30
-rw-r--r--layout/base/tests/test_bug1515822.html47
-rw-r--r--layout/base/tests/test_bug1550869_video.html35
-rw-r--r--layout/base/tests/test_bug1714640.html36
-rw-r--r--layout/base/tests/test_bug1756118.html87
-rw-r--r--layout/base/tests/test_bug332655-1.html57
-rw-r--r--layout/base/tests/test_bug332655-2.html67
-rw-r--r--layout/base/tests/test_bug369950.html91
-rw-r--r--layout/base/tests/test_bug370436.html91
-rw-r--r--layout/base/tests/test_bug386575.xhtml46
-rw-r--r--layout/base/tests/test_bug388019.html44
-rw-r--r--layout/base/tests/test_bug394057.html88
-rw-r--r--layout/base/tests/test_bug399284.html115
-rw-r--r--layout/base/tests/test_bug399951.html34
-rw-r--r--layout/base/tests/test_bug404209.xhtml47
-rw-r--r--layout/base/tests/test_bug416896.html64
-rw-r--r--layout/base/tests/test_bug423523.html104
-rw-r--r--layout/base/tests/test_bug435293-interaction.html49
-rw-r--r--layout/base/tests/test_bug435293-scale.html103
-rw-r--r--layout/base/tests/test_bug435293-skew.html173
-rw-r--r--layout/base/tests/test_bug449781.html62
-rw-r--r--layout/base/tests/test_bug450930.xhtml28
-rw-r--r--layout/base/tests/test_bug469170.html48
-rw-r--r--layout/base/tests/test_bug471126.html34
-rw-r--r--layout/base/tests/test_bug499538-1.html53
-rw-r--r--layout/base/tests/test_bug514127.html55
-rw-r--r--layout/base/tests/test_bug518777.html44
-rw-r--r--layout/base/tests/test_bug548545.xhtml47
-rw-r--r--layout/base/tests/test_bug558663.html37
-rw-r--r--layout/base/tests/test_bug559499.html26
-rw-r--r--layout/base/tests/test_bug569520.html67
-rw-r--r--layout/base/tests/test_bug582181-1.html60
-rw-r--r--layout/base/tests/test_bug582181-2.html63
-rw-r--r--layout/base/tests/test_bug582771.html128
-rw-r--r--layout/base/tests/test_bug583889.html53
-rw-r--r--layout/base/tests/test_bug588174.html67
-rw-r--r--layout/base/tests/test_bug603550.html118
-rw-r--r--layout/base/tests/test_bug607529.html119
-rw-r--r--layout/base/tests/test_bug629838.html87
-rw-r--r--layout/base/tests/test_bug644768.html62
-rw-r--r--layout/base/tests/test_bug646757.html43
-rw-r--r--layout/base/tests/test_bug66619.html62
-rw-r--r--layout/base/tests/test_bug667512.html40
-rw-r--r--layout/base/tests/test_bug677878.html54
-rw-r--r--layout/base/tests/test_bug687297.html54
-rw-r--r--layout/base/tests/test_bug696020.html47
-rw-r--r--layout/base/tests/test_bug718809.html28
-rw-r--r--layout/base/tests/test_bug725426.html23
-rw-r--r--layout/base/tests/test_bug731777.html49
-rw-r--r--layout/base/tests/test_bug749186.html40
-rw-r--r--layout/base/tests/test_bug761572.html40
-rw-r--r--layout/base/tests/test_bug770106.html24
-rw-r--r--layout/base/tests/test_bug842853-2.html56
-rw-r--r--layout/base/tests/test_bug842853.html52
-rw-r--r--layout/base/tests/test_bug849219.html50
-rw-r--r--layout/base/tests/test_bug851445.html34
-rw-r--r--layout/base/tests/test_bug851485.html86
-rw-r--r--layout/base/tests/test_bug858459.html59
-rw-r--r--layout/base/tests/test_bug93077-1.html31
-rw-r--r--layout/base/tests/test_bug93077-2.html31
-rw-r--r--layout/base/tests/test_bug93077-3.html34
-rw-r--r--layout/base/tests/test_bug93077-4.html34
-rw-r--r--layout/base/tests/test_bug93077-5.html34
-rw-r--r--layout/base/tests/test_bug93077-6.html34
-rw-r--r--layout/base/tests/test_bug970964.html46
-rw-r--r--layout/base/tests/test_bug977003.html32
-rw-r--r--layout/base/tests/test_bug990340.html60
-rw-r--r--layout/base/tests/test_bug993936.html164
-rw-r--r--layout/base/tests/test_caret_browsing_around_form_controls.html379
-rw-r--r--layout/base/tests/test_dynamic_toolbar_max_height.html22
-rw-r--r--layout/base/tests/test_emulateMedium.html165
-rw-r--r--layout/base/tests/test_emulate_color_scheme.html40
-rw-r--r--layout/base/tests/test_event_target_iframe_oop.html177
-rw-r--r--layout/base/tests/test_event_target_radius.html422
-rw-r--r--layout/base/tests/test_frame_reconstruction_body_table.html48
-rw-r--r--layout/base/tests/test_frame_reconstruction_body_writing_mode.html34
-rw-r--r--layout/base/tests/test_frame_reconstruction_for_column_span.html77
-rw-r--r--layout/base/tests/test_frame_reconstruction_for_pseudo_elements.html74
-rw-r--r--layout/base/tests/test_frame_reconstruction_for_svg_transforms.html46
-rw-r--r--layout/base/tests/test_frame_reconstruction_scroll_restore.html82
-rw-r--r--layout/base/tests/test_getBoxQuads_convertPointRectQuad.html717
-rw-r--r--layout/base/tests/test_getClientRects_emptytext.html26
-rw-r--r--layout/base/tests/test_mozPaintCount.html62
-rw-r--r--layout/base/tests/test_partialbg.html76
-rw-r--r--layout/base/tests/test_preserve3d_sorting_hit_testing.html48
-rw-r--r--layout/base/tests/test_preserve3d_sorting_hit_testing2.html40
-rw-r--r--layout/base/tests/test_refreshDriver_hasPendingTick.html95
-rw-r--r--layout/base/tests/test_reftests_with_caret.html469
-rw-r--r--layout/base/tests/test_resize_flush.html51
-rw-r--r--layout/base/tests/test_scroll_event_ordering.html63
-rw-r--r--layout/base/tests/test_scroll_into_view_in_oopif.html17
-rw-r--r--layout/base/tests/test_scroll_selection_into_view.html97
-rw-r--r--layout/base/tests/test_scroll_space_no_range_overflow_scroll.html67
-rw-r--r--layout/base/tests/test_synthmousemove.html31
-rw-r--r--layout/base/tests/test_transformed_scrolling_repaints.html54
-rw-r--r--layout/base/tests/test_transformed_scrolling_repaints_2.html54
-rw-r--r--layout/base/tests/test_transformed_scrolling_repaints_3.html24
-rw-r--r--layout/base/tests/test_visual_viewport_in_oopif.html11
-rw-r--r--layout/base/tests/test_zoom_restore_bfcache.html139
-rw-r--r--layout/base/tests/textarea-invalid-ref.html7
-rw-r--r--layout/base/tests/textarea-maxlength-invalid-change.html25
-rw-r--r--layout/base/tests/textarea-maxlength-ui-invalid-change.html25
-rw-r--r--layout/base/tests/textarea-maxlength-ui-valid-change.html28
-rw-r--r--layout/base/tests/textarea-maxlength-valid-before-change.html15
-rw-r--r--layout/base/tests/textarea-maxlength-valid-change.html28
-rw-r--r--layout/base/tests/textarea-minlength-invalid-change.html25
-rw-r--r--layout/base/tests/textarea-minlength-ui-invalid-change.html25
-rw-r--r--layout/base/tests/textarea-minlength-ui-valid-change.html28
-rw-r--r--layout/base/tests/textarea-minlength-valid-before-change.html15
-rw-r--r--layout/base/tests/textarea-minlength-valid-change.html28
-rw-r--r--layout/base/tests/textarea-valid-ref.html7
-rw-r--r--layout/base/tests/transformed_scrolling_repaints_3_window.html46
-rw-r--r--layout/base/tests/transformed_scrolling_repaints_3_window_frame.html58
-rw-r--r--layout/base/tests/visual_viewport_in_child.html25
579 files changed, 28421 insertions, 0 deletions
diff --git a/layout/base/tests/Ahem.ttf b/layout/base/tests/Ahem.ttf
new file mode 100644
index 0000000000..ac81cb0316
--- /dev/null
+++ b/layout/base/tests/Ahem.ttf
Binary files differ
diff --git a/layout/base/tests/accessiblecaret_magnifier.html b/layout/base/tests/accessiblecaret_magnifier.html
new file mode 100644
index 0000000000..855a027725
--- /dev/null
+++ b/layout/base/tests/accessiblecaret_magnifier.html
@@ -0,0 +1,91 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<p id="display">
+ <div id="editable" contenteditable style="width: 400px; font-size: 4em;">foobarbaz</div>
+</p>
+<script>
+const SimpleTest = parent.SimpleTest;
+const is = parent.is;
+const info = parent.info;
+const isnot = parent.isnot;
+const ok = parent.ok;
+
+window.addEventListener("load", runTest);
+
+async function runTest() {
+ let target = document.getElementById("editable");
+ target.focus();
+ let targetRect = target.getBoundingClientRect();
+ let selection = window.getSelection();
+
+ // Select word then show accessible caret
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ ok(!selection.getRangeAt(0).collapsed, "Select word");
+
+ let rangeRect = selection.getRangeAt(0).getBoundingClientRect();
+ let presscaret = 0;
+ let dragcaret = 0;
+ let releasecaret = 0;
+
+ const dragStart = {
+ x: Math.round(rangeRect.left),
+ y: Math.round(rangeRect.bottom + 12)
+ };
+ const dragEnd = {
+ x: Math.round(rangeRect.left + 60),
+ y: Math.round(rangeRect.bottom + 12)
+ };
+ let handler;
+
+ let promise = new Promise(resolve => {
+ handler = function(e) {
+ info("mozcaretstatechanged is fired with " + e.reason);
+ switch (e.reason) {
+ case "presscaret":
+ is(dragStart.x, e.clientX, "dragcaret event has clientX data.");
+ is(dragStart.y, e.clientY, "dragcaret event has clientY data.");
+ presscaret++;
+ break;
+ case "dragcaret":
+ is(dragEnd.x, e.clientX, "dragcaret event has clientX data.");
+ is(dragEnd.y, e.clientY, "dragcaret event has clientY data.");
+ dragcaret++;
+ break;
+ case "releasecaret":
+ releasecaret++;
+ resolve();
+ break;
+ }
+ };
+ });
+
+ SpecialPowers.addChromeEventListener("mozcaretstatechanged", handler, true);
+
+ // Drag accessible caret
+ synthesizeTouchAtPoint(dragStart.x, dragStart.y, { type: "touchstart" });
+ synthesizeTouchAtPoint(dragEnd.x, dragEnd.y, { type: "touchmove" });
+ synthesizeTouchAtPoint(dragEnd.x, dragEnd.y, { type: "touchend" });
+
+ await promise;
+
+ SpecialPowers.removeChromeEventListener("mozcaretstatechanged", handler, true);
+
+ is(presscaret, 1, "presscaret is fired correctly");
+ is(dragcaret, 1, "presscaret is fired correctly");
+ is(releasecaret, 1, "releasecaret is fired correctly");
+
+ let newRangeRect = selection.getRangeAt(0).getBoundingClientRect();
+ isnot(rangeRect.left, newRangeRect.left,
+ "Selected range is changed by dragging accessible caret");
+
+ SimpleTest.finish();
+}
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/border_radius_hit_testing_iframe.html b/layout/base/tests/border_radius_hit_testing_iframe.html
new file mode 100644
index 0000000000..a0f7ba1b92
--- /dev/null
+++ b/layout/base/tests/border_radius_hit_testing_iframe.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<title>border-radius hit testing</title>
+<style>
+
+ body { margin: 0; }
+
+ #one, #two {
+ border-radius: 100px 60px 40px 120px / 40px 60px 60px 80px;
+ margin-bottom: 100px;
+ }
+
+ #two { overflow: hidden }
+
+ #one, #two > div {
+ height: 200px;
+ background: blue;
+ cursor: progress;
+ }
+
+ #one:hover, #two > div:hover {
+ background: fuchsia;
+ }
+
+</style>
+<body>
+<div id="one"></div>
+<div id="two"><div></div></div>
diff --git a/layout/base/tests/browser.ini b/layout/base/tests/browser.ini
new file mode 100644
index 0000000000..a05e516f40
--- /dev/null
+++ b/layout/base/tests/browser.ini
@@ -0,0 +1,36 @@
+[browser_bug617076.js]
+[browser_bug839103.js]
+support-files =
+ file_bug839103.html
+ bug839103.css
+[browser_bug1701027-1.js]
+support-files =
+ helper_bug1701027-1.html
+[browser_bug1701027-2.js]
+support-files =
+ helper_bug1701027-2.html
+[browser_bug1757410.js]
+run-if = (((os == 'mac') || (os == 'win' && os_version != '6.1' && processor == 'x86_64')) && debug)
+[browser_bug1787079.js]
+run-if = ((os == 'win' && os_version != '6.1' && processor == 'x86_64') && debug)
+[browser_disableDialogs_onbeforeunload.js]
+[browser_onbeforeunload_only_after_interaction.js]
+[browser_onbeforeunload_only_after_interaction_in_frame.js]
+[browser_scroll_into_view_in_out_of_process_iframe.js]
+support-files =
+ test_scroll_into_view_in_oopif.html
+ scroll_into_view_in_child.html
+[browser_visual_viewport_iframe.js]
+support-files =
+ test_visual_viewport_in_oopif.html
+ visual_viewport_in_child.html
+[browser_select_popup_position_in_out_of_process_iframe.js]
+skip-if =
+ (verify && (os == 'mac')) # bug 1627874
+ apple_silicon # Disabled due to bleedover with other tests when run in regular suites; passes in "failures" jobs
+ (os == 'linux' && socketprocess_networking && fission && !debug) # high frequency intermittent
+support-files =
+ !/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js
+ !/browser/base/content/test/forms/head.js
+[browser_bug1791083.js]
+skip-if = !sessionHistoryInParent
diff --git a/layout/base/tests/browser_bug1701027-1.js b/layout/base/tests/browser_bug1701027-1.js
new file mode 100644
index 0000000000..bf5a4ffaa3
--- /dev/null
+++ b/layout/base/tests/browser_bug1701027-1.js
@@ -0,0 +1,130 @@
+/* This test is based on
+ https://searchfox.org/mozilla-central/rev/e082df56bbfeaff0f388e7da9da401ff414df18f/gfx/layers/apz/test/mochitest/browser_test_select_zoom.js
+*/
+
+// In order for this test to test the original bug we need:
+// 1) At least e10s enabled so that apz is enabled so we can create an
+// nsDisplayAsyncZoom item
+// (the insertion of this item without marking the required frame modified
+// is what causes the bug in the retained display list merging)
+// 2) a root content document, again so that we can create a nsDisplayAsyncZoom
+// item
+// 3) the root content document cannot have a display port to start
+// (if it has a display port then it gets a nsDisplayAsyncZoom, but we need
+// that to be created after the anonymous content we insert into the
+// document)
+// Point 3) requires the root content document to be in the parent process,
+// since if it is in a content process it will get a displayport for being at
+// the root of a process.
+// Creating an in-process root content document I think is not possible in
+// mochitest-plain. mochitest-chrome does not have e10s enabled. So this has to
+// be a mochitest-browser-chrome test.
+
+// Outline of this test:
+// Open a new tab with a pretty simple content file, that is not scrollable
+// Use the anonymous content api to insert into that content doc
+// Send a mouse click over the content doc
+// The click hits fixed pos content.
+// This sets a displayport on the root scroll frame of the content doc.
+// (This is because we call GetAsyncScrollableAncestorFrame in
+// PrepareForSetTargetAPZCNotification
+// https://searchfox.org/mozilla-central/rev/e082df56bbfeaff0f388e7da9da401ff414df18f/gfx/layers/apz/util/APZCCallbackHelper.cpp#624
+// which passes the SCROLLABLE_FIXEDPOS_FINDS_ROOT flag
+// https://searchfox.org/mozilla-central/rev/e082df56bbfeaff0f388e7da9da401ff414df18f/layout/base/nsLayoutUtils.cpp#2884
+// so starting from fixed pos content means we always find the root scroll
+// frame, whereas if we started from non-fixed content we'd walk pass the root
+// scroll frame becase it isn't scrollable.)
+// Then we have to be careful not to do anything that causes a full display
+// list rebuild.
+// And finally we change the color of the fixed element which covers the whole
+// viewport which causes us to do a partial display list update including the
+// anonymous content, which hits the assert we are aiming to test.
+
+add_task(async function () {
+ function getChromeURL(filename) {
+ let chromeURL = getRootDirectory(gTestPath) + filename;
+ return chromeURL;
+ }
+
+ // We need this otherwise there is a burst animation on the new tab when it
+ // loads and that somehow scrolls a scroll frame, which makes it active,
+ // which makes the scrolled frame an AGR, which means we have multiple AGRs
+ // (the display port makes the root scroll frame active and an AGR) so we hit
+ // this
+ // https://searchfox.org/mozilla-central/rev/e082df56bbfeaff0f388e7da9da401ff414df18f/layout/painting/RetainedDisplayListBuilder.cpp#1179
+ // and are forced to do a full display list rebuild and that prevents us from
+ // testing the original bug.
+ await SpecialPowers.pushPrefEnv({
+ set: [["ui.prefersReducedMotion", 1]],
+ });
+
+ const pageUrl = getChromeURL("helper_bug1701027-1.html");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+ const [theX, theY] = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => {
+ content.document.body.offsetWidth;
+
+ await new Promise(r => content.window.requestAnimationFrame(r));
+
+ const rect = content.document
+ .getElementById("fd")
+ .getBoundingClientRect();
+ const x = content.window.mozInnerScreenX + rect.left + rect.width / 2;
+ const y = content.window.mozInnerScreenY + rect.top + rect.height / 2;
+
+ let doc = SpecialPowers.wrap(content.document);
+ var bq = doc.createElement("blockquote");
+ bq.textContent = "This blockquote text.";
+ var div = doc.createElement("div");
+ div.textContent = " This div text.";
+ bq.appendChild(div);
+ var ac = doc.insertAnonymousContent(bq);
+ content.document.body.offsetWidth;
+
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+
+ return [x, y];
+ }
+ );
+
+ EventUtils.synthesizeNativeMouseEvent({
+ type: "click",
+ target: window.document.documentElement,
+ screenX: theX,
+ screenY: theY,
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ });
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("fd").style.backgroundColor = "blue";
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ });
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("fd").style.backgroundColor = "red";
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ });
+
+ BrowserTestUtils.removeTab(tab);
+
+ ok(true, "didn't crash");
+});
diff --git a/layout/base/tests/browser_bug1701027-2.js b/layout/base/tests/browser_bug1701027-2.js
new file mode 100644
index 0000000000..00e55ca562
--- /dev/null
+++ b/layout/base/tests/browser_bug1701027-2.js
@@ -0,0 +1,126 @@
+/* This test is based on
+ https://searchfox.org/mozilla-central/rev/e082df56bbfeaff0f388e7da9da401ff414df18f/gfx/layers/apz/test/mochitest/browser_test_select_zoom.js
+*/
+
+// In order for this test to test the original bug we need:
+// 1) At least e10s enabled so that apz is enabled so we can create an
+// nsDisplayAsyncZoom item
+// (the insertion of this item without marking the required frame modified
+// is what causes the bug in the retained display list merging)
+// 2) a root content document, again so that we can create a nsDisplayAsyncZoom
+// item
+// 3) the root content document cannot have a display port to start
+// (if it has a display port then it gets a nsDisplayAsyncZoom, but we need
+// that to be created after the anonymous content we insert into the
+// document)
+// Point 3) requires the root content document to be in the parent process,
+// since if it is in a content process it will get a displayport for being at
+// the root of a process.
+// Creating an in-process root content document I think is not possible in
+// mochitest-plain. mochitest-chrome does not have e10s enabled. So this has to
+// be a mochitest-browser-chrome test.
+
+// Outline of this test:
+// Open a new tab with a pretty simple content file, that is not scrollable
+// Use the anonymous content api to insert into that content doc
+// Set a displayport on the root scroll frame of the content doc directly.
+// Then we have to be careful not to do anything that causes a full display
+// list rebuild.
+// And finally we change the color of the fixed element which covers the whole
+// viewport which causes us to do a partial display list update including the
+// anonymous content, which hits the assert we are aiming to test.
+
+add_task(async function () {
+ function getChromeURL(filename) {
+ let chromeURL = getRootDirectory(gTestPath) + filename;
+ return chromeURL;
+ }
+
+ // We need this otherwise there is a burst animation on the new tab when it
+ // loads and that somehow scrolls a scroll frame, which makes it active,
+ // which makes the scrolled frame an AGR, which means we have multiple AGRs
+ // (the display port makes the root scroll frame active and an AGR) so we hit
+ // this
+ // https://searchfox.org/mozilla-central/rev/e082df56bbfeaff0f388e7da9da401ff414df18f/layout/painting/RetainedDisplayListBuilder.cpp#1179
+ // and are forced to do a full display list rebuild and that prevents us from
+ // testing the original bug.
+ await SpecialPowers.pushPrefEnv({
+ set: [["ui.prefersReducedMotion", 1]],
+ });
+
+ const pageUrl = getChromeURL("helper_bug1701027-2.html");
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+ const [theX, theY] = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => {
+ content.document.body.offsetWidth;
+
+ await new Promise(r => content.window.requestAnimationFrame(r));
+
+ const rect = content.document
+ .getElementById("fd")
+ .getBoundingClientRect();
+ const x = content.window.mozInnerScreenX + rect.left + rect.width / 2;
+ const y = content.window.mozInnerScreenY + rect.top + rect.height / 2;
+
+ let doc = SpecialPowers.wrap(content.document);
+ var bq = doc.createElement("blockquote");
+ bq.textContent = "This blockquote text.";
+ var div = doc.createElement("div");
+ div.textContent = " This div text.";
+ bq.appendChild(div);
+ var ac = doc.insertAnonymousContent(bq);
+ content.document.body.offsetWidth;
+
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+
+ content.window.windowUtils.setDisplayPortMarginsForElement(
+ 0,
+ 0,
+ 0,
+ 0,
+ doc.documentElement,
+ 1
+ );
+ content.window.windowUtils.setDisplayPortBaseForElement(
+ 0,
+ 0,
+ 100,
+ 100,
+ doc.documentElement
+ );
+
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+
+ return [x, y];
+ }
+ );
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("fd").style.backgroundColor = "blue";
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ });
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.getElementById("fd").style.backgroundColor = "red";
+ });
+
+ await new Promise(resolve => setTimeout(resolve, 0));
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ await new Promise(r => content.window.requestAnimationFrame(r));
+ });
+
+ BrowserTestUtils.removeTab(tab);
+
+ ok(true, "didn't crash");
+});
diff --git a/layout/base/tests/browser_bug1757410.js b/layout/base/tests/browser_bug1757410.js
new file mode 100644
index 0000000000..59c740e5a8
--- /dev/null
+++ b/layout/base/tests/browser_bug1757410.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PAGECONTENT =
+ "<!DOCTYPE html>" +
+ "<html>" +
+ "<style>" +
+ "html { " +
+ " height: 120vh;" +
+ " overflow-y: scroll;" +
+ "}" +
+ "</style>" +
+ "</html>";
+
+const pageUrl = "data:text/html," + encodeURIComponent(PAGECONTENT);
+
+add_task(async function test() {
+ if (window.devicePixelRatio == 1) {
+ ok(
+ true,
+ "Skip this test since this test is supposed to run on HiDPI mode, " +
+ "the devixePixelRato on this machine is " +
+ window.devicePixelRatio
+ );
+ return;
+ }
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+ // Scroll the content a bit.
+ const originalScrollPosition = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => {
+ content.document.scrollingElement.scrollTop = 100;
+ return content.document.scrollingElement.scrollTop;
+ }
+ );
+
+ // Disabling HiDPI mode and check the scroll position.
+ SpecialPowers.DOMWindowUtils.setHiDPIMode(false);
+ // Make sure we restore even if this test failed.
+ registerCleanupFunction(() => {
+ SpecialPowers.DOMWindowUtils.restoreHiDPIMode();
+ });
+
+ const scrollPosition = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ async () => {
+ return content.document.scrollingElement.scrollTop;
+ }
+ );
+ is(
+ originalScrollPosition,
+ scrollPosition,
+ "The scroll position should be kept"
+ );
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/layout/base/tests/browser_bug1787079.js b/layout/base/tests/browser_bug1787079.js
new file mode 100644
index 0000000000..4ce38f8b69
--- /dev/null
+++ b/layout/base/tests/browser_bug1787079.js
@@ -0,0 +1,86 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PAGECONTENT =
+ "<!DOCTYPE html>" +
+ "<html>" +
+ "<style>" +
+ "html { height: 100vh; }" +
+ "</style>" +
+ "</html>";
+
+const pageUrl = "data:text/html," + encodeURIComponent(PAGECONTENT);
+
+add_task(async function test() {
+ SpecialPowers.DOMWindowUtils.setHiDPIMode(true);
+ registerCleanupFunction(() => {
+ SpecialPowers.DOMWindowUtils.restoreHiDPIMode();
+ });
+
+ const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, pageUrl);
+
+ // Enter fullscreen.
+ let fullscreenChangePromise = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "fullscreenchange"
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ content.document.documentElement.requestFullscreen();
+ });
+ await fullscreenChangePromise;
+
+ let [originalInnerWidth, originalInnerHeight] = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return [content.window.innerWidth, content.window.innerHeight];
+ }
+ );
+
+ // Then change the DPI.
+ let originalPixelRatio = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return content.window.devicePixelRatio;
+ }
+ );
+ let dpiChangedPromise = TestUtils.waitForCondition(async () => {
+ let pixelRatio = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ return content.window.devicePixelRatio;
+ });
+ return pixelRatio != originalPixelRatio;
+ }, "Make sure the DPI changed");
+ SpecialPowers.DOMWindowUtils.setHiDPIMode(false);
+ await dpiChangedPromise;
+
+ let [innerWidth, innerHeight] = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ () => {
+ return [content.window.innerWidth, content.window.innerHeight];
+ }
+ );
+
+ ok(
+ originalInnerWidth < innerWidth,
+ "window.innerWidth on a lower DPI should be greater than the original"
+ );
+ ok(
+ originalInnerHeight < innerHeight,
+ "window.innerHeight on a lower DPI should be greater than the original"
+ );
+
+ fullscreenChangePromise = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "fullscreenchange"
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ content.document.exitFullscreen();
+ });
+ await fullscreenChangePromise;
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/layout/base/tests/browser_bug1791083.js b/layout/base/tests/browser_bug1791083.js
new file mode 100644
index 0000000000..fcc59f5c18
--- /dev/null
+++ b/layout/base/tests/browser_bug1791083.js
@@ -0,0 +1,81 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const URL =
+ "data:text/html," +
+ "<style> a:hover {background-color: black}</style>" +
+ "<body style='width:100px;height:100px'>" +
+ "<a href='http://www.example.com'>Click Me</a>" +
+ "</body>";
+
+function isAnchorHovered(win) {
+ return SpecialPowers.spawn(
+ win.gBrowser.selectedBrowser,
+ [],
+ async function () {
+ const a = content.document.querySelector("a");
+ return a.matches(":hover");
+ }
+ );
+}
+
+add_task(async function test() {
+ let newWin = await BrowserTestUtils.openNewBrowserWindow();
+
+ // This bug is only reproducible if the cursor is out of the viewport, so
+ // we resize the window to ensure the cursor is out of the viewport.
+
+ // SynthesizeMouse isn't sufficient because it only synthesizes
+ // mouse events without actually moving the cursor permanently to a
+ // new location.
+ newWin.resizeTo(50, 50);
+
+ BrowserTestUtils.loadURIString(newWin.gBrowser.selectedBrowser, URL);
+ await BrowserTestUtils.browserLoaded(newWin.gBrowser.selectedBrowser);
+
+ await SpecialPowers.spawn(
+ newWin.gBrowser.selectedBrowser,
+ [],
+ async function () {
+ const a = content.document.querySelector("a");
+ await EventUtils.synthesizeMouseAtCenter(
+ a,
+ { type: "mousemove" },
+ content
+ );
+ }
+ );
+
+ // We've hovered the anchor element.
+ let anchorHovered = await isAnchorHovered(newWin);
+ ok(anchorHovered, "Anchor should be hovered");
+
+ let locationChange = BrowserTestUtils.waitForLocationChange(newWin.gBrowser);
+
+ // Click the anchor to navigate away
+ await SpecialPowers.spawn(
+ newWin.gBrowser.selectedBrowser,
+ [],
+ async function () {
+ const a = content.document.querySelector("a");
+ await EventUtils.synthesizeMouseAtCenter(
+ a,
+ { type: "mousedown" },
+ content
+ );
+ await EventUtils.synthesizeMouseAtCenter(a, { type: "mouseup" }, content);
+ }
+ );
+ await locationChange;
+
+ // Navigate back to the previous page which has the anchor
+ locationChange = BrowserTestUtils.waitForLocationChange(newWin.gBrowser);
+ newWin.gBrowser.selectedBrowser.goBack();
+ await locationChange;
+
+ // Hover state should be cleared upon page caching.
+ anchorHovered = await isAnchorHovered(newWin);
+ ok(!anchorHovered, "Anchor should not be hovered");
+
+ BrowserTestUtils.closeWindow(newWin);
+});
diff --git a/layout/base/tests/browser_bug617076.js b/layout/base/tests/browser_bug617076.js
new file mode 100644
index 0000000000..c76cbd41d3
--- /dev/null
+++ b/layout/base/tests/browser_bug617076.js
@@ -0,0 +1,71 @@
+/**
+ * 1. load about:addons in a new tab and select that tab
+ * 2. insert a button with tooltiptext
+ * 3. create a new blank tab and select that tab
+ * 4. select the about:addons tab and hover the inserted button
+ * 5. remove the about:addons tab
+ * 6. remove the blank tab
+ *
+ * the test succeeds if it doesn't trigger any assertions
+ */
+
+add_task(async function test() {
+ // Open the test tab
+ let testTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:addons"
+ );
+
+ // insert button into test page content
+ await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async function () {
+ let doc = content.document;
+ let e = doc.createXULElement("button");
+ e.setAttribute("label", "hello");
+ e.setAttribute("tooltiptext", "world");
+ e.setAttribute("id", "test-button");
+ doc.documentElement.insertBefore(e, doc.documentElement.firstChild);
+ });
+
+ // open a second tab and select it
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "about:blank",
+ true
+ );
+ gBrowser.selectedTab = tab2;
+
+ // Select the testTab then perform mouse events on inserted button
+ gBrowser.selectedTab = testTab;
+ let browser = gBrowser.selectedBrowser;
+ EventUtils.disableNonTestMouseEvents(true);
+ try {
+ await BrowserTestUtils.synthesizeMouse(
+ "#test-button",
+ 1,
+ 1,
+ { type: "mouseover" },
+ browser
+ );
+ await BrowserTestUtils.synthesizeMouse(
+ "#test-button",
+ 2,
+ 6,
+ { type: "mousemove" },
+ browser
+ );
+ await BrowserTestUtils.synthesizeMouse(
+ "#test-button",
+ 2,
+ 4,
+ { type: "mousemove" },
+ browser
+ );
+ } finally {
+ EventUtils.disableNonTestMouseEvents(false);
+ }
+
+ // cleanup
+ BrowserTestUtils.removeTab(testTab);
+ BrowserTestUtils.removeTab(tab2);
+ ok(true, "pass if no assertions");
+});
diff --git a/layout/base/tests/browser_bug839103.js b/layout/base/tests/browser_bug839103.js
new file mode 100644
index 0000000000..fd20b82029
--- /dev/null
+++ b/layout/base/tests/browser_bug839103.js
@@ -0,0 +1,82 @@
+const gTestRoot = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ "http://127.0.0.1:8888/"
+);
+
+add_task(async function test() {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: gTestRoot + "file_bug839103.html" },
+ async function (browser) {
+ await SpecialPowers.spawn(browser, [gTestRoot], testBody);
+ }
+ );
+});
+
+// This function runs entirely in the content process. It doesn't have access
+// any free variables in this file.
+async function testBody(testRoot) {
+ const gStyleSheet = "bug839103.css";
+
+ function unexpectedContentEvent(event) {
+ ok(false, "Received a " + event.type + " event on content");
+ }
+
+ // We've seen the original stylesheet in the document.
+ // Now add a stylesheet on the fly and make sure we see it.
+ let doc = content.document;
+ doc.styleSheetChangeEventsEnabled = true;
+ doc.addEventListener(
+ "StyleSheetApplicableStateChanged",
+ unexpectedContentEvent
+ );
+ doc.defaultView.addEventListener(
+ "StyleSheetApplicableStateChanged",
+ unexpectedContentEvent
+ );
+
+ let link = doc.createElement("link");
+ link.setAttribute("rel", "stylesheet");
+ link.setAttribute("type", "text/css");
+ link.setAttribute("href", testRoot + gStyleSheet);
+
+ let stateChanged = ContentTaskUtils.waitForEvent(
+ docShell.chromeEventHandler,
+ "StyleSheetApplicableStateChanged",
+ true
+ );
+ doc.body.appendChild(link);
+
+ info("waiting for applicable state change event");
+ let evt = await stateChanged;
+ info("received dynamic style sheet applicable state change event");
+ is(
+ evt.type,
+ "StyleSheetApplicableStateChanged",
+ "evt.type has expected value"
+ );
+ is(evt.target, doc, "event targets correct document");
+ is(evt.stylesheet, link.sheet, "evt.stylesheet has the right value");
+ is(evt.applicable, true, "evt.applicable has the right value");
+
+ stateChanged = ContentTaskUtils.waitForEvent(
+ docShell.chromeEventHandler,
+ "StyleSheetApplicableStateChanged",
+ true
+ );
+ link.sheet.disabled = true;
+
+ evt = await stateChanged;
+ is(
+ evt.type,
+ "StyleSheetApplicableStateChanged",
+ "evt.type has expected value"
+ );
+ info(
+ 'received dynamic style sheet applicable state change event after media="" changed'
+ );
+ is(evt.target, doc, "event targets correct document");
+ is(evt.stylesheet, link.sheet, "evt.stylesheet has the right value");
+ is(evt.applicable, false, "evt.applicable has the right value");
+
+ doc.body.removeChild(link);
+}
diff --git a/layout/base/tests/browser_disableDialogs_onbeforeunload.js b/layout/base/tests/browser_disableDialogs_onbeforeunload.js
new file mode 100644
index 0000000000..684fbf1d10
--- /dev/null
+++ b/layout/base/tests/browser_disableDialogs_onbeforeunload.js
@@ -0,0 +1,64 @@
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+function pageScript() {
+ window.addEventListener(
+ "beforeunload",
+ function (event) {
+ var str = "Some text that causes the beforeunload dialog to be shown";
+ event.returnValue = str;
+ return str;
+ },
+ true
+ );
+}
+
+SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", false]],
+});
+
+const PAGE_URL =
+ "data:text/html," +
+ encodeURIComponent("<script>(" + pageScript.toSource() + ")();</script>");
+
+add_task(async function enableDialogs() {
+ // The onbeforeunload dialog should appear.
+ let dialogPromise = PromptTestUtils.waitForPrompt(null, {
+ modalType: Services.prompt.MODAL_TYPE_CONTENT,
+ promptType: "confirmEx",
+ });
+
+ let openPagePromise = openPage(true);
+ let dialog = await dialogPromise;
+ Assert.ok(true, "Showed the beforeunload dialog.");
+
+ await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 0 });
+ await openPagePromise;
+});
+
+add_task(async function disableDialogs() {
+ // The onbeforeunload dialog should NOT appear.
+ await openPage(false);
+ info("If we time out here, then the dialog was shown...");
+});
+
+async function openPage(enableDialogs) {
+ // Open about:blank in a new tab.
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async function (browser) {
+ // Load the page.
+ BrowserTestUtils.loadURIString(browser, PAGE_URL);
+ await BrowserTestUtils.browserLoaded(browser);
+ // Load the content script in the frame.
+ let methodName = enableDialogs ? "enableDialogs" : "disableDialogs";
+ await SpecialPowers.spawn(browser, [methodName], async function (name) {
+ content.windowUtils[name]();
+ });
+ // And then navigate away.
+ BrowserTestUtils.loadURIString(browser, "http://example.com/");
+ await BrowserTestUtils.browserLoaded(browser);
+ }
+ );
+}
diff --git a/layout/base/tests/browser_onbeforeunload_only_after_interaction.js b/layout/base/tests/browser_onbeforeunload_only_after_interaction.js
new file mode 100644
index 0000000000..af60db1acf
--- /dev/null
+++ b/layout/base/tests/browser_onbeforeunload_only_after_interaction.js
@@ -0,0 +1,75 @@
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+function pageScript() {
+ window.addEventListener(
+ "beforeunload",
+ function (event) {
+ var str = "Some text that causes the beforeunload dialog to be shown";
+ event.returnValue = str;
+ return str;
+ },
+ true
+ );
+}
+
+SpecialPowers.pushPrefEnv({
+ set: [["dom.require_user_interaction_for_beforeunload", true]],
+});
+
+const PAGE_URL =
+ "data:text/html," +
+ encodeURIComponent("<script>(" + pageScript.toSource() + ")();</script>");
+
+add_task(async function doClick() {
+ // The onbeforeunload dialog should appear.
+ let dialogPromise = PromptTestUtils.waitForPrompt(null, {
+ modalType: Services.prompt.MODAL_TYPE_CONTENT,
+ promptType: "confirmEx",
+ });
+
+ let openPagePromise = openPage(true);
+ let dialog = await dialogPromise;
+ Assert.ok(true, "Showed the beforeunload dialog.");
+
+ await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 0 });
+ await openPagePromise;
+});
+
+add_task(async function noClick() {
+ // The onbeforeunload dialog should NOT appear.
+ await openPage(false);
+ info("If we time out here, then the dialog was shown...");
+});
+
+async function openPage(shouldClick) {
+ // Open about:blank in a new tab.
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async function (browser) {
+ // Load the page.
+ BrowserTestUtils.loadURIString(browser, PAGE_URL);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ if (shouldClick) {
+ await BrowserTestUtils.synthesizeMouse("body", 2, 2, {}, browser);
+ }
+ let hasInteractedWith = await SpecialPowers.spawn(
+ browser,
+ [""],
+ function () {
+ return content.document.userHasInteracted;
+ }
+ );
+ is(
+ shouldClick,
+ hasInteractedWith,
+ "Click should update document interactivity state"
+ );
+ // And then navigate away.
+ BrowserTestUtils.loadURIString(browser, "http://example.com/");
+ await BrowserTestUtils.browserLoaded(browser);
+ }
+ );
+}
diff --git a/layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js b/layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js
new file mode 100644
index 0000000000..6f73a2ece2
--- /dev/null
+++ b/layout/base/tests/browser_onbeforeunload_only_after_interaction_in_frame.js
@@ -0,0 +1,96 @@
+const { PromptTestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/PromptTestUtils.sys.mjs"
+);
+
+function pageScript() {
+ window.addEventListener(
+ "beforeunload",
+ function (event) {
+ var str = "Some text that causes the beforeunload dialog to be shown";
+ event.returnValue = str;
+ return str;
+ },
+ true
+ );
+}
+
+SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.require_user_interaction_for_beforeunload", true],
+ ["security.allow_eval_with_system_principal", true],
+ ],
+});
+
+const FRAME_URL =
+ "data:text/html," + encodeURIComponent("<body>Just a frame</body>");
+
+const PAGE_URL =
+ "data:text/html," +
+ encodeURIComponent(
+ "<iframe src='" +
+ FRAME_URL +
+ "'></iframe><script>(" +
+ pageScript.toSource() +
+ ")();</script>"
+ );
+
+add_task(async function doClick() {
+ // The onbeforeunload dialog should appear.
+ let dialogPromise = PromptTestUtils.waitForPrompt(null, {
+ modalType: Services.prompt.MODAL_TYPE_CONTENT,
+ promptType: "confirmEx",
+ });
+
+ let openPagePromise = openPage(true);
+ let dialog = await dialogPromise;
+ Assert.ok(true, "Showed the beforeunload dialog.");
+
+ await PromptTestUtils.handlePrompt(dialog, { buttonNumClick: 0 });
+ await openPagePromise;
+});
+
+add_task(async function noClick() {
+ // The onbeforeunload dialog should NOT appear.
+ await openPage(false);
+ info("If we time out here, then the dialog was shown...");
+});
+
+async function openPage(shouldClick) {
+ // Open about:blank in a new tab.
+ await BrowserTestUtils.withNewTab(
+ { gBrowser, url: "about:blank" },
+ async function (browser) {
+ // Load the page.
+ BrowserTestUtils.loadURIString(browser, PAGE_URL);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let frameBC = browser.browsingContext.children[0];
+ if (shouldClick) {
+ await BrowserTestUtils.synthesizeMouse("body", 2, 2, {}, frameBC);
+ }
+ let hasInteractedWith = await SpecialPowers.spawn(
+ frameBC,
+ [],
+ function () {
+ return [
+ content.document.userHasInteracted,
+ content.document.userHasInteracted,
+ ];
+ }
+ );
+ is(
+ shouldClick,
+ hasInteractedWith[0],
+ "Click should update parent interactivity state"
+ );
+ is(
+ shouldClick,
+ hasInteractedWith[1],
+ "Click should update frame interactivity state"
+ );
+ // And then navigate away.
+ BrowserTestUtils.loadURIString(browser, "http://example.com/");
+ await BrowserTestUtils.browserLoaded(browser);
+ }
+ );
+}
diff --git a/layout/base/tests/browser_scroll_into_view_in_out_of_process_iframe.js b/layout/base/tests/browser_scroll_into_view_in_out_of_process_iframe.js
new file mode 100644
index 0000000000..edf926c4ca
--- /dev/null
+++ b/layout/base/tests/browser_scroll_into_view_in_out_of_process_iframe.js
@@ -0,0 +1,50 @@
+"use strict";
+
+add_task(async () => {
+ function httpURL(filename, host = "https://example.com/") {
+ let root = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ host
+ );
+ return root + filename;
+ }
+
+ const fissionWindow = await BrowserTestUtils.openNewBrowserWindow({
+ fission: true,
+ });
+ const url = httpURL(
+ "test_scroll_into_view_in_oopif.html",
+ "http://mochi.test:8888/"
+ );
+ const crossOriginIframeUrl = httpURL("scroll_into_view_in_child.html");
+
+ try {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser: fissionWindow.gBrowser, url },
+ async browser => {
+ await SpecialPowers.spawn(
+ browser,
+ [crossOriginIframeUrl],
+ async iframeUrl => {
+ const iframe = content.document.getElementById("iframe");
+ iframe.setAttribute("src", iframeUrl);
+
+ // Wait for a scroll event since scrollIntoView for cross origin documents is
+ // asyncronously processed.
+ const scroller = content.document.getElementById("scroller");
+ await new Promise(resolve => {
+ scroller.addEventListener("scroll", resolve, { once: true });
+ });
+
+ ok(
+ scroller.scrollTop > 0,
+ "scrollIntoView works in a cross origin iframe"
+ );
+ }
+ );
+ }
+ );
+ } finally {
+ await BrowserTestUtils.closeWindow(fissionWindow);
+ }
+});
diff --git a/layout/base/tests/browser_select_popup_position_in_out_of_process_iframe.js b/layout/base/tests/browser_select_popup_position_in_out_of_process_iframe.js
new file mode 100644
index 0000000000..c118c1c726
--- /dev/null
+++ b/layout/base/tests/browser_select_popup_position_in_out_of_process_iframe.js
@@ -0,0 +1,122 @@
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+ this
+);
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/browser/base/content/test/forms/head.js",
+ this
+);
+
+const PAGECONTENT_TRANSLATED =
+ "<html><body>" +
+ "<div id='div'>" +
+ "<iframe id='frame' width='320' height='295' style='margin: 100px;'" +
+ " src='https://example.com/document-builder.sjs?html=<html><select id=select><option>he he he</option><option>boo boo</option><option>baz baz</option></select></html>'" +
+ "</iframe>" +
+ "</div></body></html>";
+
+function openSelectPopup(x, y, win) {
+ const popupShownPromise = BrowserTestUtils.waitForSelectPopupShown(win);
+ EventUtils.synthesizeNativeMouseEvent({
+ type: "click",
+ target: win.document.documentElement,
+ screenX: x,
+ screenY: y,
+ });
+ return popupShownPromise;
+}
+
+add_task(async function () {
+ const pageUrl = "data:text/html," + escape(PAGECONTENT_TRANSLATED);
+
+ const newWin = await BrowserTestUtils.openNewBrowserWindow({ fission: true });
+
+ const browserLoadedPromise = BrowserTestUtils.browserLoaded(
+ newWin.gBrowser.selectedBrowser,
+ true /* includeSubFrames */
+ );
+ BrowserTestUtils.loadURIString(newWin.gBrowser.selectedBrowser, pageUrl);
+ await browserLoadedPromise;
+
+ newWin.gBrowser.selectedBrowser.focus();
+
+ const tab = newWin.gBrowser.selectedTab;
+
+ // We need to explicitly call Element.focus() since dataURL is treated as
+ // cross-origin, thus autofocus doesn't work there.
+ const iframeBC = await SpecialPowers.spawn(tab.linkedBrowser, [], () => {
+ return content.document.querySelector("iframe").browsingContext;
+ });
+
+ const [iframeBorderLeft, iframeBorderTop, iframeX, iframeY] =
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async function () {
+ await SpecialPowers.contentTransformsReceived(content);
+ const iframe = content.document.querySelector("iframe");
+ const rect = iframe.getBoundingClientRect();
+ const x = content.window.mozInnerScreenX + rect.left;
+ const y = content.window.mozInnerScreenY + rect.top;
+ const cs = content.window.getComputedStyle(iframe);
+ return [parseInt(cs.borderLeftWidth), parseInt(cs.borderTopWidth), x, y];
+ });
+
+ const selectRect = await SpecialPowers.spawn(iframeBC, [], async function () {
+ await SpecialPowers.contentTransformsReceived(content);
+ const input = content.document.getElementById("select");
+ const focusPromise = new Promise(resolve => {
+ input.addEventListener("focus", resolve, { once: true });
+ });
+ input.focus();
+ await focusPromise;
+ return input.getBoundingClientRect();
+ });
+
+ // Open the select popup.
+ const selectPopup = await openSelectPopup(
+ iframeX + selectRect.x + selectRect.width / 2,
+ iframeY + selectRect.y + selectRect.height / 2,
+ newWin
+ );
+
+ // Check the coordinates of 'selectPopup'.
+ let popupRect = selectPopup.getBoundingClientRect();
+ is(
+ popupRect.x,
+ iframeX +
+ iframeBorderLeft +
+ selectRect.x -
+ newWin.mozInnerScreenX +
+ parseFloat(getComputedStyle(selectPopup).marginLeft),
+ "x position of the popup"
+ );
+
+ let expectedYPosition =
+ iframeY +
+ selectRect.y +
+ iframeBorderTop -
+ newWin.mozInnerScreenY +
+ parseFloat(getComputedStyle(selectPopup).marginTop);
+
+ // On platforms other than macOS the popup menu is positioned below the
+ // option element. On macOS the top is aligned to the selected item (so the
+ // first label).
+ if (navigator.platform.includes("Mac")) {
+ const offsetToSelectedItem =
+ selectPopup.querySelector("menuitem[selected]").getBoundingClientRect()
+ .top - popupRect.top;
+ expectedYPosition -= offsetToSelectedItem;
+ } else {
+ expectedYPosition += selectRect.height;
+ }
+
+ isfuzzy(
+ popupRect.y,
+ expectedYPosition,
+ window.devicePixelRatio,
+ "y position of the popup"
+ );
+
+ await hideSelectPopup("enter", newWin);
+
+ await BrowserTestUtils.closeWindow(newWin);
+});
diff --git a/layout/base/tests/browser_visual_viewport_iframe.js b/layout/base/tests/browser_visual_viewport_iframe.js
new file mode 100644
index 0000000000..849ab53caa
--- /dev/null
+++ b/layout/base/tests/browser_visual_viewport_iframe.js
@@ -0,0 +1,59 @@
+"use strict";
+
+add_task(async () => {
+ function httpURL(filename, host = "https://example.com/") {
+ let root = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content/",
+ host
+ );
+ return root + filename;
+ }
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.meta-viewport.enabled", true]],
+ });
+
+ const fissionWindow = await BrowserTestUtils.openNewBrowserWindow({
+ fission: true,
+ });
+ const url = httpURL(
+ "test_visual_viewport_in_oopif.html",
+ "http://mochi.test:8888/"
+ );
+ const crossOriginIframeUrl = httpURL("visual_viewport_in_child.html");
+
+ try {
+ await BrowserTestUtils.withNewTab(
+ { gBrowser: fissionWindow.gBrowser, url },
+ async browser => {
+ await SpecialPowers.spawn(
+ browser,
+ [crossOriginIframeUrl],
+ async iframeUrl => {
+ const iframe = content.document.getElementById("iframe");
+ iframe.setAttribute("src", iframeUrl);
+
+ let { width, height } = await new Promise(resolve => {
+ content.window.addEventListener("message", msg => {
+ resolve(msg.data);
+ });
+ });
+
+ is(
+ width,
+ 300,
+ "visualViewport.width shouldn't be affected in out-of-process iframes"
+ );
+ is(
+ height,
+ 300,
+ "visualViewport.height shouldn't be affected in out-of-process iframes"
+ );
+ }
+ );
+ }
+ );
+ } finally {
+ await BrowserTestUtils.closeWindow(fissionWindow);
+ }
+});
diff --git a/layout/base/tests/bug1007065-1-ref.html b/layout/base/tests/bug1007065-1-ref.html
new file mode 100644
index 0000000000..fe867dfa0b
--- /dev/null
+++ b/layout/base/tests/bug1007065-1-ref.html
@@ -0,0 +1,15 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ </head>
+ <body onload="start()">
+ <input type="text" style="text-align:right; border-width:0;">
+ <script>
+ function start() {
+ var input = document.querySelector("input");
+ input.focus();
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1007065-1.html b/layout/base/tests/bug1007065-1.html
new file mode 100644
index 0000000000..174ffb888b
--- /dev/null
+++ b/layout/base/tests/bug1007065-1.html
@@ -0,0 +1,15 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ </head>
+ <body onload="start()">
+ <input type="text" style="text-align:right; overflow:hidden; border-width:0;">
+ <script>
+ function start() {
+ var input = document.querySelector("input");
+ input.focus();
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1007067-1-ref.html b/layout/base/tests/bug1007067-1-ref.html
new file mode 100644
index 0000000000..b303167dd0
--- /dev/null
+++ b/layout/base/tests/bug1007067-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="utf-8">
+</head>
+<body onload="start()">
+<textarea style="width:500px; height: 200px; border-width:0">-----------------------------------------
+{insert newline before opening bracket}-----------------------------------b</textarea>
+<select><option value="AED">AED - United Arab Emirates Dirham - د.إ</option></select>
+ <script>
+ function start() {
+ var input = document.querySelector("textarea");
+ input.selectionStart = 42
+ input.selectionEnd = 42
+ input.focus();
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1007067-1.html b/layout/base/tests/bug1007067-1.html
new file mode 100644
index 0000000000..6284d13ef2
--- /dev/null
+++ b/layout/base/tests/bug1007067-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+<meta charset="utf-8">
+</head>
+<body onload="start()">
+<textarea style="width:500px; height: 200px; border-width:0">-----------------------------------------{insert newline before opening bracket}-----------------------------------b</textarea>
+<select><option value="AED">AED - United Arab Emirates Dirham - د.إ</option></select>
+ <script>
+ function start() {
+ var input = document.querySelector("textarea");
+ input.selectionStart = 41
+ input.selectionEnd = 41
+ input.focus();
+ window.parent.synthesizeKey("VK_RETURN", { }, window)
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1061468-ref.html b/layout/base/tests/bug1061468-ref.html
new file mode 100644
index 0000000000..9a3330c9fe
--- /dev/null
+++ b/layout/base/tests/bug1061468-ref.html
@@ -0,0 +1,13 @@
+<html>
+<head></head>
+<body>
+
+<div id="firstDiv">
+Parent1
+</div>
+
+<div id="secondDiv">
+Parent2<div contenteditable id="editable">Testing 1</div></div>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug1061468.html b/layout/base/tests/bug1061468.html
new file mode 100644
index 0000000000..be538fe0ae
--- /dev/null
+++ b/layout/base/tests/bug1061468.html
@@ -0,0 +1,40 @@
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script>
+function runTest() {
+ var sel = window.getSelection();
+ var r = new Range()
+ r.setStart(document.querySelector("#firstDiv"),0);
+ r.setEnd(document.querySelector("#firstDiv"),1);
+ sel.addRange(r)
+
+ document.querySelector("#editable").focus();
+ document.querySelector("#secondDiv").appendChild(document.querySelector("#editable"));
+
+ is(sel.rangeCount, 1, "still have a range in Selection")
+ var s=""
+ try {
+ var r2 = sel.getRangeAt(0)
+ s+=r2.startContainer.tagName
+ s+=r2.startOffset
+ s+=r2.endContainer.tagName
+ s+=r2.endOffset
+ } catch(e) {}
+
+ is(s, "DIV1DIV1", "the range gravitated correctly")
+}
+</script>
+</head>
+<body onload="runTest()">
+
+<div id="firstDiv">
+Parent1
+<div contenteditable id="editable">Testing 1</div>
+</div>
+
+<div id="secondDiv">
+Parent2</div>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug106855-1-ref.html b/layout/base/tests/bug106855-1-ref.html
new file mode 100644
index 0000000000..9ed8da4fc2
--- /dev/null
+++ b/layout/base/tests/bug106855-1-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+x<br>
+<textarea id="t" rows="4" spellcheck="false" style="-moz-appearance: none">
+A
+
+
+</textarea><br>
+y
+<script>
+ // Position the caret at the last line
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+
+ var area = document.getElementById('t');
+ area.focus();
+
+ sendKey('RIGHT'); // now after "A"
+ sendKey('RIGHT'); //
+ sendKey('RIGHT'); //
+ sendKey('RIGHT'); // now at the last line
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug106855-1.html b/layout/base/tests/bug106855-1.html
new file mode 100644
index 0000000000..9bba203c8a
--- /dev/null
+++ b/layout/base/tests/bug106855-1.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+x<br>
+<textarea id="t" rows="4" spellcheck="false" style="-moz-appearance: none">
+A
+
+
+</textarea><br>
+y
+<script>
+ // Position the caret at the last line
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+
+ var area = document.getElementById('t');
+ area.focus();
+
+ sendKey('DOWN'); // now after "A"
+ sendKey('DOWN'); //
+ sendKey('DOWN'); // now at the last line
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug106855-2.html b/layout/base/tests/bug106855-2.html
new file mode 100644
index 0000000000..040d033632
--- /dev/null
+++ b/layout/base/tests/bug106855-2.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+x<br>
+<textarea id="t" rows="4" spellcheck="false" style="-moz-appearance: none">
+A
+
+
+</textarea><br>
+y
+<script>
+ // Position the caret at the last line
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+
+ var area = document.getElementById('t');
+ area.focus();
+
+ sendKey('DOWN'); // now after "A"
+ sendKey('DOWN'); //
+ sendKey('DOWN'); //
+ sendKey('DOWN'); // now at the last line
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1078327_inner.html b/layout/base/tests/bug1078327_inner.html
new file mode 100644
index 0000000000..41fe05a099
--- /dev/null
+++ b/layout/base/tests/bug1078327_inner.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1078327
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1078327</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #listener { background: yellow; padding: 10px; }
+ #mediator { background: red; padding: 20px; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var mediator = undefined;
+ var listener = undefined;
+ var test_target = false;
+ var test_capture = false;
+ var test_mediator_over = false;
+ var test_mediator_move = false;
+ var test_mediator_out = false;
+ var test_listener = false;
+ var test_lost_capture = false;
+
+ function TargetHandler(event) {
+ logger("Target receive event: " + event.type + ". Mediator.setPointerCapture()");
+ mediator.setPointerCapture(event.pointerId);
+ test_target = true;
+ test_capture = true;
+ }
+ function MediatorHandler(event) {
+ logger("Mediator receive event: " + event.type);
+ if(!test_capture)
+ return;
+ if(event.type == "pointermove")
+ test_mediator_move++;
+ if(event.type == "pointerover")
+ test_mediator_over++;
+ if(event.type == "pointerout")
+ test_mediator_out++;
+ if(event.type == "lostpointercapture")
+ test_lost_capture = true;
+ }
+ function ListenerHandler(event) {
+ logger("Listener receive event: " + event.type);
+ test_listener = true;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ mediator = document.getElementById("mediator");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetHandler);
+ mediator.addEventListener("gotpointercapture", MediatorHandler);
+ mediator.addEventListener("pointerover", MediatorHandler);
+ mediator.addEventListener("pointermove", MediatorHandler);
+ mediator.addEventListener("pointerout", MediatorHandler);
+ mediator.addEventListener("lostpointercapture", MediatorHandler);
+ listener.addEventListener("pointermove", ListenerHandler);
+ var rect_t = target.getBoundingClientRect();
+ var rect_m = mediator.getBoundingClientRect();
+ var rect_l = listener.getBoundingClientRect();
+ synthesizeMouse(target, rect_t.width/2, rect_t.height/20, {type: "mousedown"});
+ synthesizeMouse(target, rect_t.width/2, rect_t.height/20, {type: "mousemove"});
+ synthesizeMouse(mediator, rect_m.width/2, rect_m.height/20, {type: "mousemove"});
+ synthesizeMouse(listener, rect_l.width/2, rect_l.height/20, {type: "mousemove"});
+ synthesizeMouse(mediator, rect_m.width/2, rect_m.height/20, {type: "mousemove"});
+ synthesizeMouse(target, rect_t.width/2, rect_t.height/20, {type: "mousemove"});
+ synthesizeMouse(target, rect_t.width/2, rect_t.height/20, {type: "mouseup"});
+ synthesizeMouse(target, rect_t.width/2, rect_t.height/20, {type: "mousemove"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_target, true, "pointerdown event should be received by target");
+ parent.is(test_lost_capture, true, "mediator should receive lostpointercapture");
+ parent.is(test_mediator_over, 1, "mediator should receive pointerover event only once");
+ parent.is(test_mediator_move, 5, "mediator should receive pointermove event five times");
+ parent.is(test_mediator_out, 1, "mediator should receive pointerout event only once");
+ parent.is(test_listener, false, "listener should not receive any events");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1078327">Mozilla Bug 1078327</a>
+ <div id="mediator">
+ <div id="listener">div id=listener</div>
+ </div>
+ <div id="target">div id=target</div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug1080360_inner.html b/layout/base/tests/bug1080360_inner.html
new file mode 100644
index 0000000000..0d264e9dc0
--- /dev/null
+++ b/layout/base/tests/bug1080360_inner.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1080360
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1080360</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #listener { background: yellow; padding: 10px; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var listener = undefined;
+ var test_target = false;
+ var test_listener_got = false;
+ var test_listener_lost = false;
+ var test_document = false;
+
+ function TargetHandler(event) {
+ logger("Target receive event: " + event.type);
+ listener.setPointerCapture(event.pointerId);
+ test_target = true;
+ }
+ function ListenerHandler(event) {
+ logger("Listener receive event: " + event.type);
+ if(event.type == "gotpointercapture") {
+ test_listener_got = true;
+ listener.remove();
+ }
+ if(event.type == "lostpointercapture")
+ test_listener_lost = true;
+ }
+ function DocumentHandler(event) {
+ logger("Document receive event: " + event.type);
+ if(event.type == "lostpointercapture")
+ test_document = true;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetHandler);
+ listener.addEventListener("gotpointercapture", ListenerHandler);
+ listener.addEventListener("lostpointercapture", ListenerHandler);
+ document.addEventListener("lostpointercapture", DocumentHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mouseup"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_target, true, "pointerdown event should be received by target");
+ parent.is(test_listener_got, true, "gotpointercapture event should be received by listener");
+ parent.is(test_listener_lost, false, "listener should not receive lostpointercapture event");
+ parent.is(test_document, true, "document should receive lostpointercapture event");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080360">Mozilla Bug 1080360</a>
+ <div id="target">div id=target</div>
+ <div id="listener">div id=listener</div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug1080361_inner.html b/layout/base/tests/bug1080361_inner.html
new file mode 100644
index 0000000000..ba3edf9e2c
--- /dev/null
+++ b/layout/base/tests/bug1080361_inner.html
@@ -0,0 +1,114 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1080361
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1080361</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #mediator, #listener { background: yellow; margin: 10px; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var mediator = undefined;
+ var listener = undefined;
+ var test_target_down = false;
+ var test_target_up = false;
+ var test_first_exc = false;
+ var test_second_exc = false;
+ var test_third_exc = false;
+ var test_fourth_exc = false;
+ var test_listener = false;
+
+ function TargetDownHandler(event) {
+ logger("Target receive event: " + event.type);
+ test_target_down = true;
+ try {
+ logger("target.setPointerCapture()");
+ target.setPointerCapture(31415);
+ } catch(exc) {
+ test_first_exc = true;
+ parent.is(exc.name, "NotFoundError", "Exception NotFoundError should be fired");
+ }
+ try {
+ logger("mediator.setPointerCapture()");
+ mediator.remove();
+ mediator.setPointerCapture(event.pointerId);
+ } catch(exc) {
+ test_second_exc = true;
+ parent.is(exc.name, "InvalidStateError", "Exception InvalidStateError should be fired");
+ }
+ try {
+ logger("listener.setPointerCapture()");
+ listener.setPointerCapture(event.pointerId);
+ } catch(exc) {
+ test_third_exc = true;
+ }
+ }
+ function TargetUpHandler(event) {
+ logger("Target receive event: " + event.type);
+ test_target_up = true;
+ try {
+ logger("target.setPointerCapture()");
+ target.setPointerCapture(event.pointerId);
+ } catch(exc) {
+ test_fourth_exc = true;
+ }
+ }
+ function ListenerHandler(event) {
+ logger("Listener receive event: " + event.type);
+ test_listener = true;
+ listener.releasePointerCapture(event.pointerId);
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ mediator = document.getElementById("mediator");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetDownHandler);
+ target.addEventListener("pointerup", TargetUpHandler);
+ listener.addEventListener("gotpointercapture", ListenerHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mouseup"});
+ finishTest();
+ }
+ function finishTest() {
+ SimpleTest.executeSoon(function() {
+ parent.is(test_target_down, true, "pointerdown event should be received by target");
+ parent.is(test_target_up, true, "pointerup event should be received by target");
+ parent.is(test_first_exc, true, "first exception should be thrown");
+ parent.is(test_second_exc, true, "second exception should be thrown");
+ parent.is(test_third_exc, false, "third exception should not be thrown");
+ parent.is(test_fourth_exc, false, "fourth exception should not be thrown");
+ parent.is(test_listener, true, "listener should receive gotpointercapture event");
+ logger("finishTest");
+ parent.finishTest();
+ });
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1080361">Mozilla Bug 1080361</a>
+ <div id="target">div id=target</div>
+ <div id="mediator">div id=mediator</div>
+ <div id="listener">div id=listener</div>
+ <pre id="log"></pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug1082486-1-ref.html b/layout/base/tests/bug1082486-1-ref.html
new file mode 100644
index 0000000000..043779cb90
--- /dev/null
+++ b/layout/base/tests/bug1082486-1-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ /* Eliminate the blue glow when focusing the element. */
+ input {
+ background: none;
+ border: none;
+ outline: none;
+ }
+ </style>
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzil.la/1082486">Mozilla Bug 1082486</a>
+ <input id='i' value="abcdefghd" style="text-indent: -10px">
+ </body>
+</html>
diff --git a/layout/base/tests/bug1082486-1.html b/layout/base/tests/bug1082486-1.html
new file mode 100644
index 0000000000..de6801f39d
--- /dev/null
+++ b/layout/base/tests/bug1082486-1.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <style>
+ /* Eliminate the blue glow when focusing the element. */
+ input {
+ background: none;
+ border: none;
+ outline: none;
+ }
+ </style>
+ </head>
+ <body onload="focusInput();">
+ <script>
+ function focusInput() {
+ var inp = document.getElementById('i');
+ inp.focus();
+ }
+ </script>
+
+ <a target="_blank" href="https://bugzil.la/1082486">Mozilla Bug 1082486</a>
+
+ <!-- The caret will not be seen when the input is focused. -->
+ <input id='i' value="abcdefghd" style="text-indent: -10px">
+ </body>
+</html>
diff --git a/layout/base/tests/bug1082486-2-ref.html b/layout/base/tests/bug1082486-2-ref.html
new file mode 100644
index 0000000000..5b61a54b6a
--- /dev/null
+++ b/layout/base/tests/bug1082486-2-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <a target="_blank" href="https://bugzil.la/1082486">Mozilla Bug 1082486</a>
+
+ <!-- The whole input will not be seen. -->
+ <input id='i' value="abcdefghd" style="position: absolute; top: -100px; left: 0px;">
+ </body>
+</html>
diff --git a/layout/base/tests/bug1082486-2.html b/layout/base/tests/bug1082486-2.html
new file mode 100644
index 0000000000..d367883eb7
--- /dev/null
+++ b/layout/base/tests/bug1082486-2.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body onload="document.getElementById('i').focus();">
+ <a target="_blank" href="https://bugzil.la/1082486">Mozilla Bug 1082486</a>
+
+ <!-- The whole input will not be seen. -->
+ <input id='i' value="abcdefghd" style="position: absolute; top: -100px; left: 0px;">
+ </body>
+</html>
diff --git a/layout/base/tests/bug1093686_inner.html b/layout/base/tests/bug1093686_inner.html
new file mode 100644
index 0000000000..8d2b696d96
--- /dev/null
+++ b/layout/base/tests/bug1093686_inner.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<html id="html" style="height:100%">
+<head>
+ <title>Testing effect of listener on body</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .target { position:absolute; left:200px; top:200px; width:200px; height:200px; background:blue; }
+ </style>
+</head>
+<body id="body" onload="setTimeout(runTest, 0)" style="margin:0; width:100%; height:100%; overflow:hidden">
+<div id="content">
+ <div class="target" id="t"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+var eventTarget;
+window.onmousedown = function(event) { eventTarget = event.target; };
+
+// Make sure the target div is "clickable" by adding a click listener on it.
+document.getElementById('t').addEventListener('click', function(e) {
+ parent.ok(true, "target was clicked on");
+});
+
+// Helper functions
+
+function testMouseClick(aX, aY, aExpectedId, aMsg) {
+ eventTarget = null;
+ synthesizeMouseAtPoint(aX, aY, {});
+ try {
+ parent.is(eventTarget.id, aExpectedId,
+ "checking offset " + aX + "," + aY + " hit " + aExpectedId + " [" + aMsg + "]");
+ } catch (ex) {
+ parent.ok(false, "checking offset " + aX + "," + aY + " hit " + aExpectedId + " [" + aMsg + "]; got " + eventTarget);
+ }
+}
+
+function testWithAndWithoutBodyListener(aX, aY, aExpectedId, aMsg) {
+ var func = function(e) {
+ // no-op function
+ parent.ok(true, "body was clicked on");
+ };
+ testMouseClick(aX, aY, aExpectedId, aMsg + " without listener on body");
+ document.body.addEventListener("click", func);
+ testMouseClick(aX, aY, aExpectedId, aMsg + " with listener on body");
+ document.body.removeEventListener("click", func);
+}
+
+// Main tests
+
+var mm;
+function runTest() {
+ mm = SpecialPowers.getDOMWindowUtils(parent).physicalMillimeterInCSSPixels;
+ parent.ok(4*mm >= 10, "WARNING: mm " + mm + " too small in this configuration. Test results will be bogus");
+
+ // Test near the target, check it hits the target
+ testWithAndWithoutBodyListener(200 - 2*mm, 200 - 2*mm, "t", "basic click retargeting");
+ // Test on the target, check it hits the target
+ testWithAndWithoutBodyListener(200 + 2*mm, 200 + 2*mm, "t", "direct click");
+ // Test outside the target, check it hits the root
+ testWithAndWithoutBodyListener(40, 40, "body", "click way outside target");
+
+ SpecialPowers.pushPrefEnv({"set": [["ui.mouse.radius.enabled", false]]}, runTest2);
+}
+
+function runTest2() {
+ // In this test, mouse event retargeting is disabled.
+
+ // Test near the target, check it hits the body
+ testWithAndWithoutBodyListener(200 - 2*mm, 200 - 2*mm, "body", "basic click retargeting");
+ // Test on the target, check it hits the target
+ testWithAndWithoutBodyListener(200 + 2*mm, 200 + 2*mm, "t", "direct click");
+ // Test outside the target, check it hits the root
+ testWithAndWithoutBodyListener(40, 40, "body", "click way outside target");
+
+ parent.finishTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug1097242-1-ref.html b/layout/base/tests/bug1097242-1-ref.html
new file mode 100644
index 0000000000..dd62f18eec
--- /dev/null
+++ b/layout/base/tests/bug1097242-1-ref.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ function test() {
+ focus();
+ var div = document.querySelector("div");
+ div.focus();
+ getSelection().collapse(div.firstChild, 0);
+ }
+ </script>
+ <body onload="test()">
+ <div contenteditable spellcheck="false" style="outline: none">foo<span>bar</span>baz</div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1097242-1.html b/layout/base/tests/bug1097242-1.html
new file mode 100644
index 0000000000..e94a8c9917
--- /dev/null
+++ b/layout/base/tests/bug1097242-1.html
@@ -0,0 +1,22 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function focused() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ <body>
+ <div contenteditable spellcheck="false" onfocus="focused()"
+ style="outline: none">foo<span contenteditable=false
+ style="-moz-user-select: none">bar</span>baz</div>
+ <script>
+ SimpleTest.waitForFocus(() => {
+ SimpleTest.executeSoon(() => {
+ synthesizeMouseAtCenter(document.querySelector("span"), {});
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1109968-1-ref.html b/layout/base/tests/bug1109968-1-ref.html
new file mode 100644
index 0000000000..28bcf608a7
--- /dev/null
+++ b/layout/base/tests/bug1109968-1-ref.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">
+ <body onload="start()">
+ <div onfocus="done()" contenteditable>foo<div contenteditable="false"><a href="#">bar</a></div>baz</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ // Set the caret right before "baz"
+ sel.collapse(div.lastChild, 0);
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1109968-1.html b/layout/base/tests/bug1109968-1.html
new file mode 100644
index 0000000000..f5b2871168
--- /dev/null
+++ b/layout/base/tests/bug1109968-1.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <div onfocus="setTimeout(done, 0)" contenteditable>foo<div contenteditable="false"><a href="#">bar</a></div>baz</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ sel.collapse(div, 0);
+ // Press Right four times to set the caret right before "baz"
+ for (var i = 0; i < 4; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1109968-2-ref.html b/layout/base/tests/bug1109968-2-ref.html
new file mode 100644
index 0000000000..9638c35b33
--- /dev/null
+++ b/layout/base/tests/bug1109968-2-ref.html
@@ -0,0 +1,17 @@
+<html class="reftest-wait">
+ <body onload="start()">
+ <div onfocus="done()" contenteditable>foo<div contenteditable="false"><img src="image_rgrg-256x256.png" width="10" height="10"></div>bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ // Set the caret right before "bar"
+ sel.collapse(div.lastChild, 0);
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1109968-2.html b/layout/base/tests/bug1109968-2.html
new file mode 100644
index 0000000000..22416b30f0
--- /dev/null
+++ b/layout/base/tests/bug1109968-2.html
@@ -0,0 +1,23 @@
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <div onfocus="setTimeout(done, 0)" contenteditable>foo<div contenteditable="false"><img src="image_rgrg-256x256.png" width="10" height="10"></div>bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ sel.collapse(div, 0);
+ // Press Right four times to set the caret right before "bar"
+ for (var i = 0; i < 4; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1123067-1.html b/layout/base/tests/bug1123067-1.html
new file mode 100644
index 0000000000..86f30fa39c
--- /dev/null
+++ b/layout/base/tests/bug1123067-1.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase for bug 1123067</title>
+ <script>
+ function click(id) {
+ var e = document.querySelector(id);
+ synthesizeMouse(e, 1, 1, {type: "mousedown"}, window);
+ synthesizeMouse(e, 1, 1, {type: "mouseup"}, window);
+ }
+ function test() {
+ for (var i = 0; i < 5; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ synthesizeKey("KEY_ArrowLeft");
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ click('#test1')
+ }
+ </script>
+ <style>
+ div { -moz-user-select:none; }
+ div:focus { outline:1px solid black; }
+ </style>
+</head>
+<body>
+
+<div id="test1" contenteditable="true" spellcheck="false" onfocus="setTimeout(test, 0)">This text is NOT selectable.</div>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1123067-2.html b/layout/base/tests/bug1123067-2.html
new file mode 100644
index 0000000000..2d3d593ee2
--- /dev/null
+++ b/layout/base/tests/bug1123067-2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1123067</title>
+ <script>
+ function test() {
+ for (var i = 0; i < 5; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ synthesizeKey("KEY_ArrowLeft");
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ var e = document.querySelector('#test1');
+ e.focus();
+ }
+ </script>
+ <style>
+ div { -moz-user-select:none; }
+ div:focus { outline:1px solid black; }
+ </style>
+</head>
+<body>
+
+<div id="test1" contenteditable="true" spellcheck="false" onfocus="setTimeout(test,0)">This text is NOT selectable.</div>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1123067-3.html b/layout/base/tests/bug1123067-3.html
new file mode 100644
index 0000000000..54468d2804
--- /dev/null
+++ b/layout/base/tests/bug1123067-3.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait" style="-moz-user-select:none" spellcheck="false">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1123067</title>
+ <script>
+ function test() {
+ for (var i = 0; i < 5; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ synthesizeKey("KEY_ArrowLeft");
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ document.designMode='on';
+ document.querySelector('div').focus();
+ document.body.offsetHeight;
+ setTimeout(test,0)
+ }
+ </script>
+ <style>
+ div { outline:1px solid black; }
+ </style>
+</head>
+<body>
+
+<div>This text is NOT selectable.</div>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1123067-ref.html b/layout/base/tests/bug1123067-ref.html
new file mode 100644
index 0000000000..3fd5c6065f
--- /dev/null
+++ b/layout/base/tests/bug1123067-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase for bug 1123067</title>
+ <script>
+ function test() {
+ for (var i = 0; i < 5; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ synthesizeKey("KEY_ArrowLeft");
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ var e = document.querySelector('#test1');
+ e.focus();
+ }
+ </script>
+ <style>
+ div:focus { outline:1px solid black; }
+ </style>
+</head>
+<body>
+
+<div id="test1" contenteditable="true" spellcheck="false" onfocus="setTimeout(test, 0)">This text is NOT selectable.</div>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1132768-1-ref.html b/layout/base/tests/bug1132768-1-ref.html
new file mode 100644
index 0000000000..0fea14cf0a
--- /dev/null
+++ b/layout/base/tests/bug1132768-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+ <script>
+ function test() {
+ focus();
+ getSelection().selectAllChildren(document.querySelector("span"));
+ }
+ </script>
+ <body onload="test()">
+ <div>foo<span>bar</span>baz</div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1132768-1.html b/layout/base/tests/bug1132768-1.html
new file mode 100644
index 0000000000..9b062b7d85
--- /dev/null
+++ b/layout/base/tests/bug1132768-1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <title>Can drag-select non-editable content inside editable content</title>
+ <script src="selection-utils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <div contenteditable spellcheck="false"
+ style="outline: none">foo<span contenteditable=false>bar</span>baz</div>
+ <script>
+ SimpleTest.waitForFocus(function() {
+ const span = document.querySelector("span");
+ const rect = span.getBoundingClientRect();
+ dragSelectPoints(span, 0, rect.height / 2, rect.width, rect.height / 2);
+ setTimeout(() => document.documentElement.removeAttribute("class"));
+ });
+ </script>
+</html>
diff --git a/layout/base/tests/bug1153130_inner.html b/layout/base/tests/bug1153130_inner.html
new file mode 100644
index 0000000000..aee8b6792a
--- /dev/null
+++ b/layout/base/tests/bug1153130_inner.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1153130
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1153130</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target { background: yellow; padding: 10px; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var test_down = false;
+ var test_capture = false;
+ var test_move = false;
+ var test_success = false;
+
+ function TargetHandler(event) {
+ logger("Target receive event: " + event.type);
+ if(event.type == "pointerdown") {
+ test_down = true;
+ target.setPointerCapture(event.pointerId);
+ } else if(event.type == "gotpointercapture") {
+ test_capture = true;
+ } else if(event.type == "pointermove" && test_capture) {
+ test_move = true;
+ }
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('target');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest() {
+ logger("executeTest");
+ target = document.getElementById("target");
+ target.addEventListener("pointerdown", TargetHandler);
+ target.addEventListener("gotpointercapture", TargetHandler);
+ target.addEventListener("pointermove", TargetHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/5, rect.height/5, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/5, rect.height/5, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/4, rect.height/4, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/3, rect.height/3, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/3, rect.height/3, {type: "mouseup"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousemove"});
+ test_success = true;
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_down, true, "pointerdown event should be received by target");
+ parent.is(test_capture, true, "gotpointercapture event should be received by target");
+ parent.is(test_move, true, "pointermove event should be received by target while pointer capture is active");
+ parent.is(test_success, true, "Firefox should be live!");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1153130">Mozilla Bug 1153130</a>
+ <div id="target">div id=target</div>
+</body>
+</html>
diff --git a/layout/base/tests/bug1162990_inner_1.html b/layout/base/tests/bug1162990_inner_1.html
new file mode 100644
index 0000000000..fc6a855c61
--- /dev/null
+++ b/layout/base/tests/bug1162990_inner_1.html
@@ -0,0 +1,146 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1162990
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1162990</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ div#basket {
+ background: red;
+ padding: 10px;
+ margin: 10px;
+ }
+ div#target {
+ background: lightgreen;
+ padding: 10px;
+ margin: 10px;
+ }
+ div#child {
+ background: lightblue;
+ padding: 10px;
+ margin: 10px;
+ }
+ div#listener {
+ background: yellow;
+ padding: 10px;
+ margin: 10px;
+ }
+ </style>
+ <script type="application/javascript">
+ var basket;
+ var target;
+ var child;
+ var listener;
+
+ var test_basketLeave = 0;
+ var test_targetGotCapture = 0;
+ var test_targetLostCapture = 0;
+ var test_targetLeave = 0;
+ var test_childLeave = 0;
+ var test_listenerDown = 0;
+ var test_listenerLeave = 0;
+
+ function basketLeaveHandler(event) {
+ logger("basket: " + event.type);
+ test_basketLeave++;
+ }
+ function targetGotHandler(event) {
+ logger("target: " + event.type);
+ test_targetGotCapture++;
+ }
+ function targetLostHandler(event) {
+ logger("target: " + event.type);
+ test_targetLostCapture++;
+ }
+ function targetLeaveHandler(event) {
+ logger("target: " + event.type);
+ test_targetLeave++;
+ }
+ function childLeaveHandler(event) {
+ logger("child: " + event.type);
+ test_childLeave++;
+ }
+ function listenerDownHandler(event) {
+ logger("listener: " + event.type);
+ target.setPointerCapture(event.pointerId);
+ test_listenerDown++;
+ }
+ function listenerLeaveHandler(event) {
+ logger("listener: " + event.type);
+ test_listenerLeave++;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+
+ function setEventHandlers() {
+ basket = document.getElementById("basket");
+ target = document.getElementById("target");
+ child = document.getElementById("child");
+ listener = document.getElementById("listener");
+
+ basket.addEventListener("pointerleave", basketLeaveHandler);
+ target.addEventListener("gotpointercapture", targetGotHandler);
+ target.addEventListener("lostpointercapture", targetLostHandler);
+ target.addEventListener("pointerleave", targetLeaveHandler);
+ child.addEventListener("pointerleave", childLeaveHandler);
+ listener.addEventListener("pointerdown", listenerDownHandler);
+ listener.addEventListener("pointerleave", listenerLeaveHandler);
+ }
+
+ function executeTest()
+ {
+ logger("executeTest");
+ setEventHandlers();
+ var rectCd = child.getBoundingClientRect();
+ var rectLr = listener.getBoundingClientRect();
+ synthesizeMouse(listener, rectLr.width/3, rectLr.height/2, {type: "mousedown"});
+ synthesizeMouse(child, rectCd.width/3, rectCd.height/2, {type: "mousemove"});
+ synthesizeMouse(listener, rectLr.width/3, rectLr.height/2, {type: "mousemove"});
+ synthesizeMouse(child, rectCd.width/3, rectCd.height/2, {type: "mousemove"});
+ synthesizeMouse(listener, rectLr.width/3, rectLr.height/2, {type: "mousemove"});
+ synthesizeMouse(listener, rectLr.width/3, rectLr.height/2, {type: "mouseup"});
+ synthesizeMouse(listener, rectLr.width/3, rectLr.height/3, {type: "mousemove"});
+ finishTest();
+ }
+
+ function finishTest() {
+ parent.is(test_basketLeave, 1, "Part1: basket should receive pointerleave event after pointer capturing");
+ parent.is(test_targetGotCapture, 1, "Part1: target should receive gotpointercapture event");
+ parent.is(test_targetLostCapture, 1, "Part1: target should receive lostpointercapture event");
+ parent.is(test_targetLeave, 1, "Part1: target should receive pointerleave event only one time");
+ parent.is(test_childLeave, 0, "Part1: child should not receive pointerleave event after pointer capturing");
+ parent.is(test_listenerDown, 1, "Part1: listener should receive pointerdown event");
+ parent.is(test_listenerLeave, 1, "Part1: listener should receive pointerleave event only one time");
+ logger("finishTest");
+ parent.finishTest();
+ }
+
+ function logger(message) {
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1162990">Mozilla Bug 1162990 Test 1</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="basket">div id=basket
+ <div id="target">div id=target
+ <div id="child">div id=child</div>
+ </div>
+ </div>
+ <div id="listener">div id=listener</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug1162990_inner_2.html b/layout/base/tests/bug1162990_inner_2.html
new file mode 100644
index 0000000000..2125860f28
--- /dev/null
+++ b/layout/base/tests/bug1162990_inner_2.html
@@ -0,0 +1,147 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1162990
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1162990</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ div#basket {
+ background: red;
+ padding: 10px;
+ margin: 10px;
+ }
+ div#target {
+ background: lightgreen;
+ padding: 10px;
+ margin: 10px;
+ }
+ div#child {
+ background: lightblue;
+ padding: 10px;
+ margin: 10px;
+ }
+ div#listener {
+ background: yellow;
+ padding: 10px;
+ margin: 10px;
+ }
+ </style>
+ <script type="application/javascript">
+ var basket;
+ var target;
+ var child;
+ var listener;
+
+ var test_basketLeave = 0;
+ var test_targetDown = 0;
+ var test_targetGotCapture = 0;
+ var test_targetLostCapture = 0;
+ var test_targetLeave = 0;
+ var test_childLeave = 0;
+ var test_listenerLeave = 0;
+
+ function basketLeaveHandler(event) {
+ logger("basket: " + event.type);
+ test_basketLeave++;
+ }
+ function targetDownHandler(event) {
+ logger("target: " + event.type);
+ target.setPointerCapture(event.pointerId);
+ test_targetDown++;
+ }
+ function targetGotHandler(event) {
+ logger("target: " + event.type);
+ test_targetGotCapture++;
+ }
+ function targetLostHandler(event) {
+ logger("target: " + event.type);
+ test_targetLostCapture++;
+ }
+ function targetLeaveHandler(event) {
+ logger("target: " + event.type);
+ test_targetLeave++;
+ }
+ function childLeaveHandler(event) {
+ logger("child: " + event.type);
+ test_childLeave++;
+ }
+ function listenerLeaveHandler(event) {
+ logger("listener: " + event.type);
+ test_listenerLeave++;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+
+ function setEventHandlers() {
+ basket = document.getElementById("basket");
+ target = document.getElementById("target");
+ child = document.getElementById("child");
+ listener = document.getElementById("listener");
+
+ basket.addEventListener("pointerleave", basketLeaveHandler);
+ target.addEventListener("pointerdown", targetDownHandler);
+ target.addEventListener("gotpointercapture", targetGotHandler);
+ target.addEventListener("lostpointercapture", targetLostHandler);
+ target.addEventListener("pointerleave", targetLeaveHandler);
+ child.addEventListener("pointerleave", childLeaveHandler);
+ listener.addEventListener("pointerleave", listenerLeaveHandler);
+ }
+
+ function executeTest()
+ {
+ logger("executeTest");
+ setEventHandlers();
+ var rectTg = target.getBoundingClientRect();
+ var rectCd = child.getBoundingClientRect();
+ var rectLr = listener.getBoundingClientRect();
+ synthesizeMouse(target, rectTg.width/3, rectTg.height/7, {type: "mousedown"});
+ synthesizeMouse(child, rectCd.width/3, rectCd.height/2, {type: "mousemove"});
+ synthesizeMouse(listener, rectLr.width/3, rectLr.height/2, {type: "mousemove"});
+ synthesizeMouse(child, rectCd.width/3, rectCd.height/2, {type: "mousemove"});
+ synthesizeMouse(target, rectTg.width/3, rectTg.height/7, {type: "mousemove"});
+ synthesizeMouse(target, rectTg.width/3, rectTg.height/7, {type: "mouseup"});
+ synthesizeMouse(target, rectTg.width/3, rectTg.height/9, {type: "mousemove"});
+ finishTest();
+ }
+
+ function finishTest() {
+ parent.is(test_basketLeave, 0, "Part2: basket should not receive pointerleave event after pointer capturing");
+ parent.is(test_targetDown, 1, "Part2: target should receive pointerdown event");
+ parent.is(test_targetGotCapture, 1, "Part2: target should receive gotpointercapture event");
+ parent.is(test_targetLostCapture, 1, "Part2: target should receive lostpointercapture event");
+ parent.is(test_targetLeave, 0, "Part2: target should not receive pointerleave event");
+ parent.is(test_childLeave, 0, "Part2: child should not receive pointerleave event after pointer capturing");
+ parent.is(test_listenerLeave, 0, "Part2: listener should not receive pointerleave event after pointer capturing");
+ logger("finishTest");
+ parent.finishTest();
+ }
+
+ function logger(message) {
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1162990">Mozilla Bug 1162990 Test 2</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="basket">div id=basket
+ <div id="target">div id=target
+ <div id="child">div id=child</div>
+ </div>
+ </div>
+ <div id="listener">div id=listener</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug1226904.html b/layout/base/tests/bug1226904.html
new file mode 100644
index 0000000000..646608b1c1
--- /dev/null
+++ b/layout/base/tests/bug1226904.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=684759
+-->
+<head>
+ <title>Test for Bug 684759</title>
+</head>
+<style>
+ #container {
+ transform-style: preserve-3d;
+ transform: translate3d(400px, 0px, 10px);
+ background-color: black;
+ }
+ #separator {
+ width: 400px;
+ height: 400px;
+ background-color: green;
+ }
+ #transformed {
+ width: 400px;
+ height: 400px;
+ background-color: blue;
+ transform: translate3d(-400px, 0px, 10px);
+ }
+</style>
+<body onload="run()">
+<div>
+ <div id="container">
+ <div id="separator"></div>
+ <div id="transformed"></div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/layout/base/tests/bug1237236-1-ref.html b/layout/base/tests/bug1237236-1-ref.html
new file mode 100644
index 0000000000..eed0fafe8f
--- /dev/null
+++ b/layout/base/tests/bug1237236-1-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1237236</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowDown"); // move caret to the second line
+ // caret should now be at the start of the second line
+ document.body.offsetHeight;
+ setTimeout(function(){ document.documentElement.removeAttribute("class"); },0);
+ }
+ function runTests() {
+ document.querySelector('textarea').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<textarea onfocus="test1()" spellcheck="false" style="-moz-appearance:none">abc
+def</textarea>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1237236-1.html b/layout/base/tests/bug1237236-1.html
new file mode 100644
index 0000000000..e7ea00a15a
--- /dev/null
+++ b/layout/base/tests/bug1237236-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1237236</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowDown", {shiftKey: true}); // select first line including the newline
+ synthesizeKey("KEY_ArrowRight"); // collapse to the end of the selection
+ // caret should now be at the start of the second line
+ document.body.offsetHeight;
+ setTimeout(function(){ document.documentElement.removeAttribute("class"); },0);
+ }
+ function runTests() {
+ document.querySelector('textarea').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<textarea onfocus="test1()" spellcheck="false" style="-moz-appearance:none">abc
+def</textarea>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1237236-2-ref.html b/layout/base/tests/bug1237236-2-ref.html
new file mode 100644
index 0000000000..8b4ccece76
--- /dev/null
+++ b/layout/base/tests/bug1237236-2-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1237236</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowRight", {repeat: 4});
+ document.body.offsetHeight;
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ document.querySelector('pre').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<pre contenteditable tabindex=1 onfocus="setTimeout(test1, 0)" spellcheck="false">abc
+def</pre>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1237236-2.html b/layout/base/tests/bug1237236-2.html
new file mode 100644
index 0000000000..dea4f93600
--- /dev/null
+++ b/layout/base/tests/bug1237236-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1237236</title>
+ <script>
+ function test1() {
+ document.body.offsetHeight;
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ var pre = document.querySelector('pre');
+ window.getSelection().collapse(pre.firstChild, 4/*after the newline*/)
+ pre.focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<pre contenteditable tabindex=1 onfocus="test1()" spellcheck="false">abc
+def</pre>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1258308-1-ref.html b/layout/base/tests/bug1258308-1-ref.html
new file mode 100644
index 0000000000..a84c7e4d95
--- /dev/null
+++ b/layout/base/tests/bug1258308-1-ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1258308</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ // caret should now be at the start of the third line
+ document.body.offsetHeight;
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ document.documentElement.removeAttribute("class");
+ }));
+ }
+ function runTests() {
+ document.querySelector('textarea').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<textarea onfocus="test1()" spellcheck="false" style="-moz-appearance:none;scrollbar-width:none">abc
+def
+ghi</textarea>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1258308-1.html b/layout/base/tests/bug1258308-1.html
new file mode 100644
index 0000000000..e6234ca818
--- /dev/null
+++ b/layout/base/tests/bug1258308-1.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1258308</title>
+ <script>
+ function test1() {
+ const kIsMac = navigator.platform.indexOf("Mac") == 0;
+ synthesizeKey("KEY_ArrowDown"); // go to the second line
+ // go to the end of the second line
+ if (kIsMac) {
+ synthesizeKey("KEY_ArrowRight", {accelKey: true});
+ } else {
+ synthesizeKey("KEY_End");
+ }
+ synthesizeKey("KEY_ArrowRight", {shiftKey: true}); // select the newline
+ synthesizeKey("KEY_ArrowRight"); // collapse to the end of the selection
+ // caret should now be at the start of the third line
+ document.body.offsetHeight;
+ requestAnimationFrame(() => requestAnimationFrame(() => {
+ document.documentElement.removeAttribute("class");
+ }));
+ }
+ function runTests() {
+ document.querySelector('textarea').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<textarea onfocus="test1()" spellcheck="false" style="-moz-appearance:none;scrollbar-width:none">abc
+def
+ghi</textarea>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1258308-2-ref.html b/layout/base/tests/bug1258308-2-ref.html
new file mode 100644
index 0000000000..3bd51f0f0e
--- /dev/null
+++ b/layout/base/tests/bug1258308-2-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1258308</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowRight", {repeat: 8});
+ document.body.offsetHeight;
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ document.querySelector('pre').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<pre contenteditable tabindex=1 onfocus="setTimeout(test1, 0)" spellcheck="false">abc
+def
+ghi</pre>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1258308-2.html b/layout/base/tests/bug1258308-2.html
new file mode 100644
index 0000000000..e88ded5f6c
--- /dev/null
+++ b/layout/base/tests/bug1258308-2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1258308</title>
+ <script>
+ function test1() {
+ document.body.offsetHeight;
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ var pre = document.querySelector('pre');
+ window.getSelection().collapse(pre.firstChild, 8/*after the 2nd line newline*/)
+ pre.focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<pre contenteditable tabindex=1 onfocus="test1()" spellcheck="false">abc
+def
+ghi</pre>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1259949-1-ref.html b/layout/base/tests/bug1259949-1-ref.html
new file mode 100644
index 0000000000..7e46c43c28
--- /dev/null
+++ b/layout/base/tests/bug1259949-1-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1259949</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowDown");
+ // caret should now be at the start of the second line
+ document.body.offsetHeight;
+ setTimeout(function(){ document.documentElement.removeAttribute("class"); },0);
+ }
+ function runTests() {
+ document.querySelector('textarea').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<textarea onfocus="test1()" spellcheck="false" style="-moz-appearance:none">abcdef
+</textarea>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1259949-1.html b/layout/base/tests/bug1259949-1.html
new file mode 100644
index 0000000000..0be791a54b
--- /dev/null
+++ b/layout/base/tests/bug1259949-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1259949</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowRight");
+ synthesizeKey("KEY_ArrowRight");
+ synthesizeKey("KEY_ArrowRight"); // caret is now between "c" and "d"
+ synthesizeKey("KEY_ArrowDown", {shiftKey: true}); // select "def\n"
+ synthesizeKey("KEY_ArrowRight"); // collapse to the end of the selection
+ // caret should now be at the start of the second line
+ document.body.offsetHeight;
+ setTimeout(function(){ document.documentElement.removeAttribute("class"); },0);
+ }
+ function runTests() {
+ document.querySelector('textarea').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<textarea onfocus="test1()" spellcheck="false" style="-moz-appearance:none">abcdef
+</textarea>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1259949-2-ref.html b/layout/base/tests/bug1259949-2-ref.html
new file mode 100644
index 0000000000..2a1f686ef3
--- /dev/null
+++ b/layout/base/tests/bug1259949-2-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1259949</title>
+ <script>
+ function test1() {
+ synthesizeKey("KEY_ArrowRight", {repeat: 7});
+ document.body.offsetHeight;
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ document.querySelector('pre').focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body>
+
+<pre contenteditable tabindex=1 onfocus="setTimeout(test1, 0)" spellcheck="false">abcdef
+
+</pre>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1259949-2.html b/layout/base/tests/bug1259949-2.html
new file mode 100644
index 0000000000..ce4b61fe29
--- /dev/null
+++ b/layout/base/tests/bug1259949-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1259949</title>
+ <script>
+ function test1() {
+ document.body.offsetHeight;
+ document.documentElement.removeAttribute("class");
+ }
+ function runTests() {
+ var pre = document.querySelector('pre');
+ window.getSelection().collapse(pre, 1/*after the text*/)
+ pre.focus();
+ document.body.offsetHeight;
+ }
+ </script>
+</head>
+<body onload="runTests()">
+
+<pre contenteditable tabindex=1 onfocus="test1()" spellcheck="false">abcdef
+<br></pre>
+
+<script>
+SimpleTest.waitForFocus(runTests);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263288-ref.html b/layout/base/tests/bug1263288-ref.html
new file mode 100644
index 0000000000..aef3e45d5d
--- /dev/null
+++ b/layout/base/tests/bug1263288-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263288
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1263288</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="readonly">xyz</div>
+<div id="editable" spellcheck=false contenteditable='true' style="outline: 1px solid;"><span>xyz</span><br></br></div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 1);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263288.html b/layout/base/tests/bug1263288.html
new file mode 100644
index 0000000000..e89452c1df
--- /dev/null
+++ b/layout/base/tests/bug1263288.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263288
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1263288</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="readonly">xyz</div>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span contentEditable='false'>xyz<!-- comment --></span><br></br></div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 0);
+ synthesizeMouse(theDiv, 100, 2, {});
+ synthesizeKey("KEY_End");
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-1-ref.html b/layout/base/tests/bug1263357-1-ref.html
new file mode 100644
index 0000000000..a846a28983
--- /dev/null
+++ b/layout/base/tests/bug1263357-1-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><p id="theP"><tt>xyz<br></tt><br></p></div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theP = document.getElementById("theP");
+ theDiv.focus();
+ sel.collapse(theP, 1);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-1.html b/layout/base/tests/bug1263357-1.html
new file mode 100644
index 0000000000..bf620a7717
--- /dev/null
+++ b/layout/base/tests/bug1263357-1.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=bug1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><p><tt>xyz</tt><br></p></div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 0);
+ if (navigator.platform.indexOf("Win") == 0) {
+ synthesizeKey("KEY_End");
+ } else {
+ // End key doesn't work as expected on Mac and Linux.
+ sel.modify("move", "right", "lineboundary");
+ }
+ synthesizeKey("KEY_Enter", {shiftKey: true});
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-2-ref.html b/layout/base/tests/bug1263357-2-ref.html
new file mode 100644
index 0000000000..9f3844fcde
--- /dev/null
+++ b/layout/base/tests/bug1263357-2-ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><p id="theP"><font color=red><span>xyz<br></span></font><br></p></div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theP = document.getElementById("theP");
+ theDiv.focus();
+ sel.collapse(theP, 1);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-2.html b/layout/base/tests/bug1263357-2.html
new file mode 100644
index 0000000000..cf6277a3cd
--- /dev/null
+++ b/layout/base/tests/bug1263357-2.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=bug1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #2 for bug 1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><p><font color=red><span>xyz</span></font><br></p></div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 0);
+ if (navigator.platform.indexOf("Win") == 0) {
+ synthesizeKey("KEY_End");
+ } else {
+ // End key doesn't work as expected on Mac and Linux.
+ sel.modify("move", "right", "lineboundary");
+ }
+ synthesizeKey("KEY_Enter", {shiftKey: true});
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-3-ref.html b/layout/base/tests/bug1263357-3-ref.html
new file mode 100644
index 0000000000..fe3310b38a
--- /dev/null
+++ b/layout/base/tests/bug1263357-3-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #3 for bug 1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span id="theSpan">text<br></span><br>more</div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 1);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-3.html b/layout/base/tests/bug1263357-3.html
new file mode 100644
index 0000000000..5b5e405514
--- /dev/null
+++ b/layout/base/tests/bug1263357-3.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=bug1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #3 for bug1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span id="theSpan">text<br></span><br>more</div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theSpan = document.getElementById("theSpan");
+ theDiv.focus();
+ sel.collapse(theSpan, 2);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-4-ref.html b/layout/base/tests/bug1263357-4-ref.html
new file mode 100644
index 0000000000..dbd5ac1256
--- /dev/null
+++ b/layout/base/tests/bug1263357-4-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #4 for bug 1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span id="theSpan">text<br></span>more</div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 1);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-4.html b/layout/base/tests/bug1263357-4.html
new file mode 100644
index 0000000000..05ca3de575
--- /dev/null
+++ b/layout/base/tests/bug1263357-4.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=bug1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #4 for bug1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span id="theSpan">text<br></span>more</div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theSpan = document.getElementById("theSpan");
+ theDiv.focus();
+ sel.collapse(theSpan, 2);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-5-ref.html b/layout/base/tests/bug1263357-5-ref.html
new file mode 100644
index 0000000000..e98a620e46
--- /dev/null
+++ b/layout/base/tests/bug1263357-5-ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #5 for bug 1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span id="theSpan">text<br></span> more</div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theDiv.focus();
+ sel.collapse(theDiv, 1);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1263357-5.html b/layout/base/tests/bug1263357-5.html
new file mode 100644
index 0000000000..aa98d17578
--- /dev/null
+++ b/layout/base/tests/bug1263357-5.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML><html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=bug1263357
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testcase #5 for bug1263357</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div id="editable" contenteditable='true' spellcheck=false style="outline: 1px solid;"><span id="theSpan">text<br></span> more</div>
+<script>
+
+function start() {
+ var sel = window.getSelection();
+ // Focus on editable block.
+ theDiv = document.getElementById("editable");
+ theSpan = document.getElementById("theSpan");
+ theDiv.focus();
+ sel.collapse(theSpan, 2);
+}
+
+SimpleTest.waitForFocus(start);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1354478-1-ref.html b/layout/base/tests/bug1354478-1-ref.html
new file mode 100644
index 0000000000..be2af0e331
--- /dev/null
+++ b/layout/base/tests/bug1354478-1-ref.html
@@ -0,0 +1,35 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()">aa</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-1.html b/layout/base/tests/bug1354478-1.html
new file mode 100644
index 0000000000..bfec0df4e8
--- /dev/null
+++ b/layout/base/tests/bug1354478-1.html
@@ -0,0 +1,35 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()">aa</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-2-ref.html b/layout/base/tests/bug1354478-2-ref.html
new file mode 100644
index 0000000000..ba35b49820
--- /dev/null
+++ b/layout/base/tests/bug1354478-2-ref.html
@@ -0,0 +1,35 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()">دد</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-2.html b/layout/base/tests/bug1354478-2.html
new file mode 100644
index 0000000000..fac2a05097
--- /dev/null
+++ b/layout/base/tests/bug1354478-2.html
@@ -0,0 +1,35 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()">دد</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-3-ref.html b/layout/base/tests/bug1354478-3-ref.html
new file mode 100644
index 0000000000..fc3576a3de
--- /dev/null
+++ b/layout/base/tests/bug1354478-3-ref.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-rl;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()">aa</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-3.html b/layout/base/tests/bug1354478-3.html
new file mode 100644
index 0000000000..df36f23ff0
--- /dev/null
+++ b/layout/base/tests/bug1354478-3.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-rl;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()">aa</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-4-ref.html b/layout/base/tests/bug1354478-4-ref.html
new file mode 100644
index 0000000000..64d4a9722f
--- /dev/null
+++ b/layout/base/tests/bug1354478-4-ref.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-rl;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()">دد</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-4.html b/layout/base/tests/bug1354478-4.html
new file mode 100644
index 0000000000..4bf4e1ee8d
--- /dev/null
+++ b/layout/base/tests/bug1354478-4.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-rl;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()">دد</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-5-ref.html b/layout/base/tests/bug1354478-5-ref.html
new file mode 100644
index 0000000000..1a49897686
--- /dev/null
+++ b/layout/base/tests/bug1354478-5-ref.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-lr;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()">aa</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-5.html b/layout/base/tests/bug1354478-5.html
new file mode 100644
index 0000000000..02bde22e87
--- /dev/null
+++ b/layout/base/tests/bug1354478-5.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-lr;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()">aa</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-6-ref.html b/layout/base/tests/bug1354478-6-ref.html
new file mode 100644
index 0000000000..3e0e9b1623
--- /dev/null
+++ b/layout/base/tests/bug1354478-6-ref.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-lr;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()">دد</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1354478-6.html b/layout/base/tests/bug1354478-6.html
new file mode 100644
index 0000000000..badf030d67
--- /dev/null
+++ b/layout/base/tests/bug1354478-6.html
@@ -0,0 +1,38 @@
+<html class="reftest-wait">
+ <head>
+ <meta charset="utf-8">
+ <style>
+ body {
+ writing-mode: sideways-lr;
+ }
+ textarea {
+ inline-size: 200px;
+ -moz-appearance: none;
+ appearance: none;
+ unicode-bidi: plaintext;
+ }
+ div {
+ inline-size: 200px;
+ block-size: 20px;
+ position: relative;
+ inset-block-start: -20px;
+ background: silver;
+ }
+ </style>
+ <script>
+ function start() {
+ var textarea = document.querySelector("textarea");
+ textarea.selectionStart = 1; // place caret between the letters
+ textarea.selectionEnd = 1;
+ textarea.focus();
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()">دد</textarea>
+ <div></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1359411-ref.html b/layout/base/tests/bug1359411-ref.html
new file mode 100644
index 0000000000..939a2f4e63
--- /dev/null
+++ b/layout/base/tests/bug1359411-ref.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html><head><style>
+ i:before{content:"X"}
+</style></head><body onload="runTest()"><div><button type="button"><i></i></button><p></p></div><editor contenteditable="true">focus me, then press the UP key</editor>
+<script>
+function runTest() {
+ document.body.offsetHeight;
+ var e = document.querySelector('editor');
+ e.focus()
+}
+</script>
diff --git a/layout/base/tests/bug1359411.html b/layout/base/tests/bug1359411.html
new file mode 100644
index 0000000000..9f166dd43b
--- /dev/null
+++ b/layout/base/tests/bug1359411.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html><head><script src="/tests/SimpleTest/EventUtils.js"></script><style>
+ i:before{content:"X"}
+</style></head><body onload="runTest()"><div><button type="button"><i></i></button><p></p></div><editor contenteditable="true" onfocus="sendKey('UP')">focus me, then press the UP key</editor>
+<script>var e = document.querySelector('editor'); e.focus()</script>
+<script>
+function runTest() {
+ document.body.offsetHeight;
+ var e = document.querySelector('editor');
+ e.focus()
+}
+</script>
diff --git a/layout/base/tests/bug1415416-ref.html b/layout/base/tests/bug1415416-ref.html
new file mode 100644
index 0000000000..df833ed7bf
--- /dev/null
+++ b/layout/base/tests/bug1415416-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1415416
+-->
+<head>
+<title>Test for Bug 1415416</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+function runTest()
+{
+ sendKey("TAB");
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="runTest()">
+<input id = "text1" type="text" autofocus value=""/>
+<input id = "text2" type="text" value=""/>
+</body>
+</html>
diff --git a/layout/base/tests/bug1415416.html b/layout/base/tests/bug1415416.html
new file mode 100644
index 0000000000..e25394dc69
--- /dev/null
+++ b/layout/base/tests/bug1415416.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1415416
+-->
+<head>
+<title>Test for Bug 1415416</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+function runTest() {
+ var text = document.getElementById("text1");
+ text.readOnly = false;
+ text.value = "";
+
+ text = document.getElementById("text2");
+ text.readOnly = false;
+ text.value = "";
+
+ sendKey("TAB");
+ document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="runTest()">
+<input id = "text1" type="text" readonly autofocus value="A" />
+<input id = "text2" type="text" readonly value="B"/>
+</body>
+</html>
diff --git a/layout/base/tests/bug1423331-1-ref.html b/layout/base/tests/bug1423331-1-ref.html
new file mode 100644
index 0000000000..bc8251091b
--- /dev/null
+++ b/layout/base/tests/bug1423331-1-ref.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test reference for bug 1423331: Contenteditable insertion with pseudo-elements</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#editable {
+ outline: 1px solid black;
+ width: 300px;
+ height: 100px;
+}
+#editable::before {
+ content: "Write here";
+}
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.getElementById("editable").focus();
+ document.documentElement.className = "";
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1423331-1.html b/layout/base/tests/bug1423331-1.html
new file mode 100644
index 0000000000..354d7740cc
--- /dev/null
+++ b/layout/base/tests/bug1423331-1.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test for bug 1423331: Contenteditable insertion with pseudo-elements</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#editable {
+ outline: 1px solid black;
+ width: 300px;
+ height: 100px;
+}
+#editable::before {
+ content: "Write here";
+}
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ SimpleTest.executeSoon(() => {
+ let div = document.getElementById("editable");
+ synthesizeMouseAtCenter(div, {});
+ synthesizeMouseAtCenter(div, {});
+ document.documentElement.className ="";
+ });
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1423331-2-ref.html b/layout/base/tests/bug1423331-2-ref.html
new file mode 100644
index 0000000000..60936c48a3
--- /dev/null
+++ b/layout/base/tests/bug1423331-2-ref.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test reference for bug 1423331: Contenteditable insertion with pseudo-elements</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#editable {
+ outline: 1px solid black;
+ width: 300px;
+ height: 100px;
+}
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ let div = document.getElementById("editable");
+ div.focus();
+ sendString("xxx");
+ document.documentElement.className = "";
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1423331-2.html b/layout/base/tests/bug1423331-2.html
new file mode 100644
index 0000000000..9615a29bd4
--- /dev/null
+++ b/layout/base/tests/bug1423331-2.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test for bug 1423331: Contenteditable insertion with pseudo-elements</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#editable {
+ outline: 1px solid black;
+ width: 300px;
+ height: 100px;
+}
+#editable:empty::before {
+ content: "Write here";
+}
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ SimpleTest.executeSoon(() => {
+ let div = document.getElementById("editable");
+ synthesizeMouseAtCenter(div, {});
+ synthesizeMouseAtCenter(div, {});
+ sendString("xxx");
+ document.documentElement.className = "";
+ });
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1423331-3.html b/layout/base/tests/bug1423331-3.html
new file mode 100644
index 0000000000..5dc6df8060
--- /dev/null
+++ b/layout/base/tests/bug1423331-3.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test for bug 1423331: Contenteditable insertion with pseudo-elements</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#editable {
+ outline: 1px solid black;
+ width: 300px;
+ height: 100px;
+}
+#editable::before {
+ content: "Write here";
+}
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ let div = document.getElementById("editable");
+ synthesizeMouse(div, 2, 2, {});
+ synthesizeMouse(div, 2, 2, {});
+ document.documentElement.className = "";
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1423331-4.html b/layout/base/tests/bug1423331-4.html
new file mode 100644
index 0000000000..c1a61d41c6
--- /dev/null
+++ b/layout/base/tests/bug1423331-4.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test for bug 1423331: Contenteditable insertion with pseudo-elements</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#editable {
+ outline: 1px solid black;
+ width: 300px;
+ height: 100px;
+}
+#editable:empty::before {
+ content: "Write here";
+}
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ let div = document.getElementById("editable");
+ synthesizeMouse(div, 2, 2, {});
+ synthesizeMouse(div, 2, 2, {});
+ sendString("xxx");
+ document.documentElement.className = "";
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1448730.html b/layout/base/tests/bug1448730.html
new file mode 100644
index 0000000000..7edbd7b0f7
--- /dev/null
+++ b/layout/base/tests/bug1448730.html
@@ -0,0 +1,103 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<p id="display">
+ <input id="input">
+ <textarea id="textarea" cols="10" rows="2"></textarea>
+ <div id="editable" contenteditable style="width: 200px;"></div>
+</p>
+<div id="content" style="display: none;"></div>
+<pre id="test">
+</pre>
+<script>
+const SimpleTest = parent.SimpleTest;
+const is = parent.is;
+const isnot = parent.isnot;
+const ok = parent.ok;
+
+window.addEventListener("load", runTest);
+
+function runTest() {
+ let target = document.getElementById("input");
+ target.focus();
+
+ // <input>
+ target.value = " ";
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ is(target.selectionStart, target.value.length, "Don't select whitespace");
+ is(target.selectionEnd, target.value.length, "Don't select whitespace");
+
+ target.value = "abc";
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ is(target.selectionStart, target.value.length, "Don't select word");
+ is(target.selectionEnd, target.value.length, "Don't select word");
+
+ target.value = " ".repeat(100);
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ is(target.selectionStart, 0, "Select whitespace");
+
+ // <textarea>
+ target = document.getElementById("textarea");
+ target.value = " ";
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ is(target.selectionStart, 2, "Don't select whitespace");
+ is(target.selectionEnd, 2, "Don't select whitespace");
+
+ target.value = "abc";
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ is(target.selectionStart, target.value.length, "Don't select word");
+ is(target.selectionEnd, target.value.length, "Don't select word");
+
+ target.value = " ".repeat(10) + "\n" + " ".repeat(10) + "\n" + " ".repeat(10);
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ isnot(target.selectionStart, target.selectionEnd, "Select whitespace");
+
+ // contenteditable
+ target = document.getElementById("editable");
+ target.innerHTML = "test";
+ target.focus();
+ let range = document.createRange();
+ range.setStart(target.firstChild, target.firstChild.length);
+ range.setEnd(target.firstChild, target.firstChild.length);
+ let selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ ok(selection.getRangeAt(0).collapsed, "Don't select word");
+
+ target.innerHTML = "t".repeat(50);
+ target.focus();
+ range = document.createRange();
+ range.setStart(target.firstChild, target.firstChild.length);
+ range.setEnd(target.firstChild, target.firstChild.length);
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ synthesizeTouchAtCenter(target, { type: "touchstart" });
+ synthesizeMouseAtCenter(target, { type: "mouselongtap" });
+ synthesizeTouchAtCenter(target, { type: "touchend" });
+ ok(!selection.getRangeAt(0).collapsed, "Select word");
+
+ SimpleTest.finish();
+}
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1484094-1-ref.html b/layout/base/tests/bug1484094-1-ref.html
new file mode 100644
index 0000000000..c319fa73a4
--- /dev/null
+++ b/layout/base/tests/bug1484094-1-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <script type="text/javascript">
+function start()
+{
+ var input = document.querySelector("input");
+ input.value = '\u{1f468}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}';
+ input.addEventListener("focus", () => {
+ input.setSelectionRange(input.value.length, input.value.length);
+ document.documentElement.removeAttribute("class");
+ });
+ input.focus();
+}
+ </script>
+</head>
+<body onload="start()">
+ <input type="text"></input>
+</body>
+</html>
diff --git a/layout/base/tests/bug1484094-1.html b/layout/base/tests/bug1484094-1.html
new file mode 100644
index 0000000000..7a41c8170b
--- /dev/null
+++ b/layout/base/tests/bug1484094-1.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript">
+function start()
+{
+ var input = document.querySelector("input");
+ input.value = '\u{1f468}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}';
+ input.addEventListener("focus", () => {
+ input.setSelectionRange(0, 0);
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+ input.focus();
+}
+ </script>
+</head>
+<body onload="start()">
+ <input type="text"></input>
+</body>
+</html>
diff --git a/layout/base/tests/bug1484094-2-ref.html b/layout/base/tests/bug1484094-2-ref.html
new file mode 100644
index 0000000000..3fa9ebf91c
--- /dev/null
+++ b/layout/base/tests/bug1484094-2-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <script type="text/javascript">
+function start()
+{
+ var input = document.querySelector("input");
+ input.value = '\u{1f468}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}';
+ input.addEventListener("focus", () => {
+ input.setSelectionRange(0, 0);
+ document.documentElement.removeAttribute("class");
+ });
+ input.focus();
+}
+ </script>
+</head>
+<body onload="start()">
+ <input type="text"></input>
+</body>
+</html>
diff --git a/layout/base/tests/bug1484094-2.html b/layout/base/tests/bug1484094-2.html
new file mode 100644
index 0000000000..d5c3f92e5d
--- /dev/null
+++ b/layout/base/tests/bug1484094-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript">
+function start()
+{
+ var input = document.querySelector("input");
+ input.value = '\u{1f468}\u{200d}\u{1f469}\u{200d}\u{1f467}\u{200d}\u{1f466}';
+ input.addEventListener("focus", () => {
+ input.setSelectionRange(input.value.length, input.value.length);
+ synthesizeKey("KEY_ArrowLeft");
+ document.documentElement.removeAttribute("class");
+ });
+ input.focus();
+}
+ </script>
+</head>
+<body onload="start()">
+ <input type="text"></input>
+</body>
+</html>
diff --git a/layout/base/tests/bug1496118-ref.html b/layout/base/tests/bug1496118-ref.html
new file mode 100644
index 0000000000..af5026f985
--- /dev/null
+++ b/layout/base/tests/bug1496118-ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <style>
+ input, input:active { border: none; }
+ </style>
+</head>
+<body>
+<input id="input1" value="aaaaaaaaaaaaaaaaaaaa">
+<div id=div1 style="height: 100px;">
+ <textarea id="textarea1" style="display: none;"></textarea>
+</div>
+<script>
+SimpleTest.waitForFocus(() => {
+ let input1 = document.getElementById('input1');
+ input1.focus();
+ input1.selectionStart = input1.selectionEnd = 0;
+ synthesizeKey("A");
+ document.documentElement.removeAttribute("class");
+});
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1496118.html b/layout/base/tests/bug1496118.html
new file mode 100644
index 0000000000..cdfaf5c586
--- /dev/null
+++ b/layout/base/tests/bug1496118.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <style>
+ input, input:active { border: none; }
+ </style>
+</head>
+<body>
+<input id="input1" value="aaaaaaaaaaaaaaaaaaaa">
+<div id=div1 style="height: 100px;">
+ <textarea id="textarea1"></textarea>
+</div>
+<script>
+SimpleTest.waitForFocus(() => {
+ let div1 = document.getElementById('div1');
+ let textarea1 = document.getElementById('textarea1');
+ div1.addEventListener("drop", e => {
+ e.preventDefault();
+
+ textarea1.style = "display: none;";
+ SimpleTest.executeSoon(() => {
+ synthesizeKey("A");
+ document.documentElement.removeAttribute("class");
+ });
+ });
+
+ let input1 = document.getElementById('input1');
+ input1.focus();
+ input1.setSelectionRange(0, input1.value.length);
+
+ synthesizeDrop(input1, textarea1, [[{type: "text/plain", data: "foo"}]]);
+});
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug1506547-1.html b/layout/base/tests/bug1506547-1.html
new file mode 100644
index 0000000000..94cc07c2bf
--- /dev/null
+++ b/layout/base/tests/bug1506547-1.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Moving the caret in an editor jumps over non-editable nodes.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true">
+ I am div number one
+ <div contenteditable="false">X X X</div>
+ However I am editable
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ const noneditable = document.querySelector('div[contenteditable="false"]');
+ editable.focus();
+ synthesizeKey("KEY_ArrowDown");
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1506547-2.html b/layout/base/tests/bug1506547-2.html
new file mode 100644
index 0000000000..b9ccda446c
--- /dev/null
+++ b/layout/base/tests/bug1506547-2.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Moving the caret in an editor jumps over non-editable nodes.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true">
+ I am div number one
+ <div contenteditable="false">X X X</div>
+ However I am editable
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ // 5 words in the first line, plus the non-editable node.
+ for (let i = 0; i < "I am div number one".length + 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1506547-3.html b/layout/base/tests/bug1506547-3.html
new file mode 100644
index 0000000000..788670db50
--- /dev/null
+++ b/layout/base/tests/bug1506547-3.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Moving the caret in an editor jumps over non-editable nodes.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true">
+ I am div number one
+ <div contenteditable="false">X X X</div>
+ However I am editable
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ const noneditable = document.querySelector('div[contenteditable="false"]');
+ editable.focus();
+ synthesizeKey("KEY_End");
+ synthesizeKey("KEY_ArrowDown");
+ for (let i = 0; i < 4; ++i)
+ synthesizeKey("KEY_ArrowLeft", { ctrlKey: true });
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1506547-4-ref.html b/layout/base/tests/bug1506547-4-ref.html
new file mode 100644
index 0000000000..b013eed7f5
--- /dev/null
+++ b/layout/base/tests/bug1506547-4-ref.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Caret on editable line with non-editable content and whitespace.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true"><span>xyz </span><br>editable</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouse(editable, 100, 10, {});
+});
+</script>
diff --git a/layout/base/tests/bug1506547-4.html b/layout/base/tests/bug1506547-4.html
new file mode 100644
index 0000000000..7e83de99a9
--- /dev/null
+++ b/layout/base/tests/bug1506547-4.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<title>Caret on editable line with non-editable content and whitespace.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouse(editable, 100, 10, {});
+});
+</script>
diff --git a/layout/base/tests/bug1506547-5-ref.html b/layout/base/tests/bug1506547-5-ref.html
new file mode 100644
index 0000000000..6c0adc967f
--- /dev/null
+++ b/layout/base/tests/bug1506547-5-ref.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret on editable line with non-editable content and whitespace.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true"><span>xyz </span><br>editable</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouse(editable, 100, 10, {});
+ setTimeout(() => {
+ sendString("xxx");
+ setTimeout(() => document.documentElement.className = "");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1506547-5.html b/layout/base/tests/bug1506547-5.html
new file mode 100644
index 0000000000..f06e72fbe2
--- /dev/null
+++ b/layout/base/tests/bug1506547-5.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret on editable line with non-editable content and whitespace.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouse(editable, 100, 10, {});
+ setTimeout(() => {
+ sendString("xxx");
+ setTimeout(() => document.documentElement.className = "");
+ });
+});
+</script>
+</html>
diff --git a/layout/base/tests/bug1506547-6.html b/layout/base/tests/bug1506547-6.html
new file mode 100644
index 0000000000..d6a780e48a
--- /dev/null
+++ b/layout/base/tests/bug1506547-6.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret on editable line with non-editable content and whitespace.</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ * { outline: none }
+
+ div {
+ border: 1px solid red;
+ margin: 5px;
+ padding: 2px;
+ }
+</style>
+<div contenteditable="true"><span contenteditable="false">xyz </span><br>editable</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowLeft");
+ setTimeout(() => {
+ sendString("xxx");
+ setTimeout(() => document.documentElement.className = "");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1510942-1-ref.html b/layout/base/tests/bug1510942-1-ref.html
new file mode 100644
index 0000000000..2bafe4cc63
--- /dev/null
+++ b/layout/base/tests/bug1510942-1-ref.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test reference</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ #editable {
+ height: 300px;
+ width: 100px;
+ outline: 1px solid black;
+ }
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.getElementById("editable").focus();
+ document.documentElement.className ="";
+});
+</script>
diff --git a/layout/base/tests/bug1510942-1.html b/layout/base/tests/bug1510942-1.html
new file mode 100644
index 0000000000..35f90ef75a
--- /dev/null
+++ b/layout/base/tests/bug1510942-1.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test for bug 1510942: Caret in a transformed empty block</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ #editable {
+ height: 300px;
+ width: 100px;
+ outline: 1px solid black;
+ transform: translateZ(0);
+ }
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.getElementById("editable").focus();
+ document.documentElement.className ="";
+});
+</script>
diff --git a/layout/base/tests/bug1510942-2-ref.html b/layout/base/tests/bug1510942-2-ref.html
new file mode 100644
index 0000000000..11185e8f0c
--- /dev/null
+++ b/layout/base/tests/bug1510942-2-ref.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test reference</title>
+<!-- intentionally blank -->
diff --git a/layout/base/tests/bug1510942-2.html b/layout/base/tests/bug1510942-2.html
new file mode 100644
index 0000000000..63f067bf30
--- /dev/null
+++ b/layout/base/tests/bug1510942-2.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Test for bug 1510942: Caret in a transformed empty block</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ #editable {
+ height: 300px;
+ width: 100px;
+ outline: 1px solid black;
+ opacity: 0;
+ }
+</style>
+<div id="editable" contenteditable></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.getElementById("editable").focus();
+ document.documentElement.className ="";
+});
+</script>
diff --git a/layout/base/tests/bug1516963-1-ref.html b/layout/base/tests/bug1516963-1-ref.html
new file mode 100644
index 0000000000..2e09115cc6
--- /dev/null
+++ b/layout/base/tests/bug1516963-1-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on an empty grid container.</title>
+ <style>
+ #container {
+ display: grid;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <body>
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-1.html b/layout/base/tests/bug1516963-1.html
new file mode 100644
index 0000000000..4731472c13
--- /dev/null
+++ b/layout/base/tests/bug1516963-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty grid container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: grid;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-2-ref.html b/layout/base/tests/bug1516963-2-ref.html
new file mode 100644
index 0000000000..eb5f393e8b
--- /dev/null
+++ b/layout/base/tests/bug1516963-2-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on an empty flex container.</title>
+ <style>
+ #container {
+ display: flex;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <body>
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-2.html b/layout/base/tests/bug1516963-2.html
new file mode 100644
index 0000000000..a546f7955f
--- /dev/null
+++ b/layout/base/tests/bug1516963-2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty flex container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: flex;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-3-ref.html b/layout/base/tests/bug1516963-3-ref.html
new file mode 100644
index 0000000000..4cb022c0e4
--- /dev/null
+++ b/layout/base/tests/bug1516963-3-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on a grid container.</title>
+ <style>
+ #container {
+ display: grid;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ button {
+ height: 40px;
+ width: 60px;
+ }
+ </style>
+ <body>
+ <div id="container"><button>Grid</button></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-3.html b/layout/base/tests/bug1516963-3.html
new file mode 100644
index 0000000000..9ae289ae27
--- /dev/null
+++ b/layout/base/tests/bug1516963-3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on a grid container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: grid;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ button {
+ height: 40px;
+ width: 60px;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"><button>Grid</button></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-4-ref.html b/layout/base/tests/bug1516963-4-ref.html
new file mode 100644
index 0000000000..f069588d18
--- /dev/null
+++ b/layout/base/tests/bug1516963-4-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on a flex container.</title>
+ <style>
+ #container {
+ display: flex;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ button {
+ height: 40px;
+ width: 60px;
+ }
+ </style>
+ <body>
+ <div id="container"><button>Flex</button></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-4.html b/layout/base/tests/bug1516963-4.html
new file mode 100644
index 0000000000..c1bea13352
--- /dev/null
+++ b/layout/base/tests/bug1516963-4.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on a flex container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: flex;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ button {
+ height: 40px;
+ width: 60px;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"><button>Flex</button></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-5-ref.html b/layout/base/tests/bug1516963-5-ref.html
new file mode 100644
index 0000000000..2814863de2
--- /dev/null
+++ b/layout/base/tests/bug1516963-5-ref.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on an empty table container.</title>
+ <style>
+ #container {
+ display: table;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <body>
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-5.html b/layout/base/tests/bug1516963-5.html
new file mode 100644
index 0000000000..77510d0118
--- /dev/null
+++ b/layout/base/tests/bug1516963-5.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty table container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: table;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-6-ref.html b/layout/base/tests/bug1516963-6-ref.html
new file mode 100644
index 0000000000..c30483ce57
--- /dev/null
+++ b/layout/base/tests/bug1516963-6-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on a table container.</title>
+ <style>
+ #container {
+ display: table;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ button {
+ height: 40px;
+ width: 60px;
+ }
+ </style>
+ <body>
+ <div id="container"><button>Table</button></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1516963-6.html b/layout/base/tests/bug1516963-6.html
new file mode 100644
index 0000000000..e9add6924b
--- /dev/null
+++ b/layout/base/tests/bug1516963-6.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on a table container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: table;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ button {
+ height: 40px;
+ width: 60px;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"><button>Table</button></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1518339-1-ref.html b/layout/base/tests/bug1518339-1-ref.html
new file mode 100644
index 0000000000..7f479aaec8
--- /dev/null
+++ b/layout/base/tests/bug1518339-1-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Test reference</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<div contenteditable="true">
+ Can you edit <b>me</b>?
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouseAtCenter(editable.querySelector("b"), {});
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
diff --git a/layout/base/tests/bug1518339-1.html b/layout/base/tests/bug1518339-1.html
new file mode 100644
index 0000000000..72444a26f8
--- /dev/null
+++ b/layout/base/tests/bug1518339-1.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>contenteditable is selectable by default, even with a user-select: none ancestor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ :root {
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+ }
+</style>
+<div contenteditable="true">
+ Can you edit <b>me</b>?
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouseAtCenter(editable.querySelector("b"), {});
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
diff --git a/layout/base/tests/bug1518339-2-ref.html b/layout/base/tests/bug1518339-2-ref.html
new file mode 100644
index 0000000000..108962bc79
--- /dev/null
+++ b/layout/base/tests/bug1518339-2-ref.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Test reference</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div {
+ -webkit-user-select: all;
+ -moz-user-select: all;
+ user-select: all;
+ }
+</style>
+<div contenteditable="true">
+ <div>
+ You should be able to select <b>all</b> of me.
+ </div>
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouseAtCenter(editable.querySelector("b"), {});
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
diff --git a/layout/base/tests/bug1518339-2.html b/layout/base/tests/bug1518339-2.html
new file mode 100644
index 0000000000..0b62b12680
--- /dev/null
+++ b/layout/base/tests/bug1518339-2.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>user-select can be overriden on a contenteditable element</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div {
+ -webkit-user-select: all;
+ -moz-user-select: all;
+ user-select: all;
+ }
+</style>
+<div contenteditable="true">
+ You should be able to select <b>all</b> of me.
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ const editable = document.querySelector('div[contenteditable="true"]');
+ editable.focus();
+ synthesizeMouseAtCenter(editable.querySelector("b"), {});
+ setTimeout(() => document.documentElement.removeAttribute("class"), 0);
+});
+</script>
diff --git a/layout/base/tests/bug1524266-1-ref.html b/layout/base/tests/bug1524266-1-ref.html
new file mode 100644
index 0000000000..be5cc8ff9b
--- /dev/null
+++ b/layout/base/tests/bug1524266-1-ref.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret doesn't get stuck in a select element inside an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus-within {
+ outline: 3px solid blue;
+ }
+ span {
+ outline: none;
+ }
+</style>
+<div>
+ xx
+ <select>
+ <option value="">Placeholder</option>
+ </select>
+ <span contenteditable="true" spellcheck="false">xxx</span>
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1524266-1.html b/layout/base/tests/bug1524266-1.html
new file mode 100644
index 0000000000..4a011e6892
--- /dev/null
+++ b/layout/base/tests/bug1524266-1.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret doesn't get stuck in a select element inside an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx
+ <select>
+ <option value="">Placeholder</option>
+ </select>
+ xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ for (let i = 0; i < 7; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1524266-2-ref.html b/layout/base/tests/bug1524266-2-ref.html
new file mode 100644
index 0000000000..99024c4b83
--- /dev/null
+++ b/layout/base/tests/bug1524266-2-ref.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Can delete non-selectable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xxxxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1524266-2.html b/layout/base/tests/bug1524266-2.html
new file mode 100644
index 0000000000..459bd12cb5
--- /dev/null
+++ b/layout/base/tests/bug1524266-2.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Can delete non-selectable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+ /* <select> has user-select: none in the UA sheet, but just in case */
+ select {
+ -moz-user-select: none;
+ user-select: none;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx
+ <select>
+ <option value="">Placeholder</option>
+ </select>
+ xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ // Select whitespace + <select> + whitespace.
+ for (let i = 0; i < 3; ++i)
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ // Rip it off.
+ synthesizeKey("KEY_Delete");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1524266-3.html b/layout/base/tests/bug1524266-3.html
new file mode 100644
index 0000000000..39708d00ba
--- /dev/null
+++ b/layout/base/tests/bug1524266-3.html
@@ -0,0 +1,37 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Can delete non-selectable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+ span {
+ -moz-user-select: none;
+ user-select: none;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx
+ <span>
+ NOT EDITABLE
+ </span>
+ xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ // Select whitespace + <span>
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ // Rip it off.
+ synthesizeKey("KEY_Delete");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1524266-4.html b/layout/base/tests/bug1524266-4.html
new file mode 100644
index 0000000000..1b0eb35a07
--- /dev/null
+++ b/layout/base/tests/bug1524266-4.html
@@ -0,0 +1,30 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Can delete non-editable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx
+ <span contenteditable="false">NOT EDITABLE</span>xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ // Select whitespace + <span>
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ // Rip it off.
+ synthesizeKey("KEY_Delete");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1529492-1-ref.html b/layout/base/tests/bug1529492-1-ref.html
new file mode 100644
index 0000000000..ae643b2b92
--- /dev/null
+++ b/layout/base/tests/bug1529492-1-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+.parent {
+ font: 32px monospace;
+ width: 26ch;
+ transform: scaleX(0.5);
+ transform-origin: 0 0;
+}
+#child {
+ outline: none;
+}
+</style>
+<div class="parent"><div id="child">.BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz</div></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ let r = document.createRange();
+ let t = child.firstChild;
+ r.setStart(t, 30);
+ r.setEnd(t, 30);
+
+ let sel = getSelection();
+ sel.empty();
+ sel.addRange(r);
+
+ document.documentElement.removeAttribute("class");
+});
+</script>
diff --git a/layout/base/tests/bug1529492-1.html b/layout/base/tests/bug1529492-1.html
new file mode 100644
index 0000000000..f01e769dd0
--- /dev/null
+++ b/layout/base/tests/bug1529492-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<!-- This test relies on caret browsing being enabled. -->
+<html class="reftest-wait">
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+.parent {
+ font: 32px monospace;
+ width: 26ch;
+ overflow: hidden;
+}
+#child {
+ transform: scaleX(0.5);
+ transform-origin: 0 0;
+ outline: none;
+}
+</style>
+<div class="parent"><div id="child">.BCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz</div></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ let r = document.createRange();
+ let t = child.firstChild;
+
+ // With a scale of 0.5, the bug manifests by limiting the caret's position
+ // to half way through the text. Place the selection somewhere past that.
+ r.setStart(t, 30);
+ r.setEnd(t, 30);
+
+ let sel = getSelection();
+ sel.empty();
+ sel.addRange(r);
+
+ document.documentElement.removeAttribute("class");
+});
+</script>
diff --git a/layout/base/tests/bug1550869-1-ref.html b/layout/base/tests/bug1550869-1-ref.html
new file mode 100644
index 0000000000..9a7f0712d6
--- /dev/null
+++ b/layout/base/tests/bug1550869-1-ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on an image.</title>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ img {
+ width: 60px;
+ height: 40px;
+ }
+ </style>
+ <body>
+ <div id="container"><img src="chrome/blue-32x32.png"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-1a.html b/layout/base/tests/bug1550869-1a.html
new file mode 100644
index 0000000000..d19de01d5c
--- /dev/null
+++ b/layout/base/tests/bug1550869-1a.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on a draggable image.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ img {
+ width: 60px;
+ height: 40px;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let img = document.getElementById("img");
+ synthesizeMouseAtCenter(img, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"><img id="img" src="chrome/blue-32x32.png"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-1b.html b/layout/base/tests/bug1550869-1b.html
new file mode 100644
index 0000000000..c1cd7fa70e
--- /dev/null
+++ b/layout/base/tests/bug1550869-1b.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an undraggable image.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ img {
+ width: 60px;
+ height: 40px;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let img = document.getElementById("img");
+ synthesizeMouseAtCenter(img, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"><img id="img" src="chrome/blue-32x32.png" draggable="false"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-1c.html b/layout/base/tests/bug1550869-1c.html
new file mode 100644
index 0000000000..b18c835616
--- /dev/null
+++ b/layout/base/tests/bug1550869-1c.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an undraggable image button.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ input {
+ border: 0;
+ outline: 0;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let img = document.getElementById("img");
+ synthesizeMouseAtCenter(img, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"><input draggable="false" width="60px" height="40px" type="image" src="chrome/blue-32x32.png" id="img"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-2-ref.html b/layout/base/tests/bug1550869-2-ref.html
new file mode 100644
index 0000000000..e4215edebc
--- /dev/null
+++ b/layout/base/tests/bug1550869-2-ref.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963 Reference: Test AccessibleCaret doesn't show when clicking on an empty container.</title>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <body>
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-2a.html b/layout/base/tests/bug1550869-2a.html
new file mode 100644
index 0000000000..6cf97bf443
--- /dev/null
+++ b/layout/base/tests/bug1550869-2a.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty inline-grid container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: inline-grid;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-2b.html b/layout/base/tests/bug1550869-2b.html
new file mode 100644
index 0000000000..d6140a11a5
--- /dev/null
+++ b/layout/base/tests/bug1550869-2b.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty inline-flex container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: inline-flex;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-2c.html b/layout/base/tests/bug1550869-2c.html
new file mode 100644
index 0000000000..97de29305c
--- /dev/null
+++ b/layout/base/tests/bug1550869-2c.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty inline-table container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ display: inline-table;
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <div id="container"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1550869-2d.html b/layout/base/tests/bug1550869-2d.html
new file mode 100644
index 0000000000..7515078a81
--- /dev/null
+++ b/layout/base/tests/bug1550869-2d.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty svg container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ outline: 0;
+ }
+ </style>
+ <script>
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => { document.documentElement.removeAttribute("class"); });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <svg id="container"></svg>
+ </body>
+</html>
diff --git a/layout/base/tests/bug1591282-1-ref.html b/layout/base/tests/bug1591282-1-ref.html
new file mode 100644
index 0000000000..ad1cbec754
--- /dev/null
+++ b/layout/base/tests/bug1591282-1-ref.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret doesn't get occluded by background-color of inline child</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<span>xxx</span>
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1591282-1.html b/layout/base/tests/bug1591282-1.html
new file mode 100644
index 0000000000..8e5e0b3f16
--- /dev/null
+++ b/layout/base/tests/bug1591282-1.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret doesn't get occluded by background-color of inline child</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+ span {
+ /* This is the point of the test */
+ background-color: white
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<span>xxx</span>
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ for (let i = 0; i < 2; ++i)
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1611661-ref.html b/layout/base/tests/bug1611661-ref.html
new file mode 100644
index 0000000000..5c9d2d674e
--- /dev/null
+++ b/layout/base/tests/bug1611661-ref.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Test reference</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<div>
+ <input type="number" value="33">
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ requestAnimationFrame(function() {
+ // Move after the 3
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.className = "";
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1611661.html b/layout/base/tests/bug1611661.html
new file mode 100644
index 0000000000..d92ae05e66
--- /dev/null
+++ b/layout/base/tests/bug1611661.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Can edit input type=number with a user-select: none ancestor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<div style="user-select: none">
+ <input type="number" value="322223">
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ requestAnimationFrame(function() {
+ // Move after the 3
+ synthesizeKey("KEY_ArrowRight");
+ // Select "2222"
+ for (let i = 0; i < 4; ++i)
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ // Rip it off.
+ synthesizeKey("KEY_Delete");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634543-1-ref.html b/layout/base/tests/bug1634543-1-ref.html
new file mode 100644
index 0000000000..a65a6b4a31
--- /dev/null
+++ b/layout/base/tests/bug1634543-1-ref.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Reference</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false"><br></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634543-1.html b/layout/base/tests/bug1634543-1.html
new file mode 100644
index 0000000000..039d93df64
--- /dev/null
+++ b/layout/base/tests/bug1634543-1.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly position for block whose only child is an abspos pseudo</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+ div::before {
+ content: "";
+ /* This is the point of the test */
+ position: absolute;
+ }
+</style>
+<div contenteditable="true" spellcheck="false"></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634543-2.html b/layout/base/tests/bug1634543-2.html
new file mode 100644
index 0000000000..fb05a4e4bb
--- /dev/null
+++ b/layout/base/tests/bug1634543-2.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly position for block whose only child is an abspos span</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+ span {
+ /* This is the point of the test */
+ position: absolute;
+ }
+</style>
+<div contenteditable="true" spellcheck="false"><span></span></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634543-3.html b/layout/base/tests/bug1634543-3.html
new file mode 100644
index 0000000000..547c1c3559
--- /dev/null
+++ b/layout/base/tests/bug1634543-3.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly position for block whose only child is an empty span</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false"><span></span></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634543-4.html b/layout/base/tests/bug1634543-4.html
new file mode 100644
index 0000000000..4f82ea65b8
--- /dev/null
+++ b/layout/base/tests/bug1634543-4.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly position for block whose only child is an empty pseudo</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+ div::before {
+ content: "";
+ }
+</style>
+<div contenteditable="true" spellcheck="false"></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634743-1-ref.html b/layout/base/tests/bug1634743-1-ref.html
new file mode 100644
index 0000000000..55d0366359
--- /dev/null
+++ b/layout/base/tests/bug1634743-1-ref.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Test reference</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div {
+ outline: 2px solid blue;
+ line-height: 5;
+ border: 1px solid gray;
+ padding: 8px;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">abc</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1634743-1.html b/layout/base/tests/bug1634743-1.html
new file mode 100644
index 0000000000..1c41b2a012
--- /dev/null
+++ b/layout/base/tests/bug1634743-1.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly position for block whose only child is a non-empty pseudo, if line-height is used</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 2px solid blue;
+ }
+
+ div {
+ line-height: 5;
+ border: 1px solid gray;
+ padding: 8px;
+ }
+ div::before {
+ content: "abc";
+ }
+</style>
+<div contenteditable="true" spellcheck="false"></div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1637476-1-ref.html b/layout/base/tests/bug1637476-1-ref.html
new file mode 100644
index 0000000000..a56137aca9
--- /dev/null
+++ b/layout/base/tests/bug1637476-1-ref.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Reference: Caret is correctly painted with placeholder opacity:1</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+input {
+ -webkit-appearance:none; /* to avoid bug 1637804 */
+ /* reset any UA styles and use colors that won't trigger anti-aliasing issues */
+ background: white;
+ border: 1px solid black;
+ outline: 1px solid black;
+}
+</style>
+<input>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1637476-1.html b/layout/base/tests/bug1637476-1.html
new file mode 100644
index 0000000000..7bc32fa031
--- /dev/null
+++ b/layout/base/tests/bug1637476-1.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted with placeholder opacity:1</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+input::placeholder {
+ background: white;
+ opacity: 1;
+}
+input {
+ -webkit-appearance:none; /* to avoid bug 1637804 */
+ /* reset any UA styles and use colors that won't trigger anti-aliasing issues */
+ background: white;
+ border: 1px solid black;
+ outline: 1px solid black;
+}
+</style>
+<input placeholder="&nbsp;">
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ setTimeout(function(){document.documentElement.removeAttribute("class");}, 0);
+});
+</script>
diff --git a/layout/base/tests/bug1637476-2-ref.html b/layout/base/tests/bug1637476-2-ref.html
new file mode 100644
index 0000000000..da06bfafc2
--- /dev/null
+++ b/layout/base/tests/bug1637476-2-ref.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Reference: Caret is correctly painted with placeholder opacity:1</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+input {
+ -webkit-appearance:none; /* to avoid bug 1637804 */
+ /* reset any UA styles and use colors that won't trigger anti-aliasing issues */
+ background: white;
+ border: 1px solid black;
+ outline: 1px solid black;
+}
+</style>
+<input type=password>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1637476-2.html b/layout/base/tests/bug1637476-2.html
new file mode 100644
index 0000000000..3fb69f5dca
--- /dev/null
+++ b/layout/base/tests/bug1637476-2.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted with placeholder opacity:1</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+input::placeholder {
+ background: white;
+ opacity: 1;
+}
+input {
+ -webkit-appearance:none; /* to avoid bug 1637804 */
+ /* reset any UA styles and use colors that won't trigger anti-aliasing issues */
+ background: white;
+ border: 1px solid black;
+ outline: 1px solid black;
+}
+</style>
+<input type=password placeholder="&nbsp;">
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ setTimeout(function(){document.documentElement.removeAttribute("class");}, 0);
+});
+</script>
diff --git a/layout/base/tests/bug1637476-3-ref.html b/layout/base/tests/bug1637476-3-ref.html
new file mode 100644
index 0000000000..ca4b9d97ef
--- /dev/null
+++ b/layout/base/tests/bug1637476-3-ref.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Reference: Caret is correctly painted with placeholder opacity:1</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+input {
+ -webkit-appearance:none; /* to avoid bug 1637804 */
+ /* reset any UA styles and use colors that won't trigger anti-aliasing issues */
+ background: white;
+ border: 1px solid black;
+ outline: 1px solid black;
+}
+</style>
+<input type=number>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ requestAnimationFrame(function() {
+ requestAnimationFrame(function() {
+ document.documentElement.removeAttribute("class");
+ });
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1637476-3.html b/layout/base/tests/bug1637476-3.html
new file mode 100644
index 0000000000..a133a07f2e
--- /dev/null
+++ b/layout/base/tests/bug1637476-3.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted with placeholder opacity:1</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+input::placeholder {
+ background: white;
+ opacity: 1;
+}
+input {
+ -webkit-appearance:none; /* to avoid bug 1637804 */
+ /* reset any UA styles and use colors that won't trigger anti-aliasing issues */
+ background: white;
+ border: 1px solid black;
+ outline: 1px solid black;
+}
+</style>
+<input type=number placeholder="&nbsp;">
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('input').focus();
+ setTimeout(function(){document.documentElement.removeAttribute("class");}, 0);
+});
+</script>
diff --git a/layout/base/tests/bug1663475-1-ref.html b/layout/base/tests/bug1663475-1-ref.html
new file mode 100644
index 0000000000..180a5aa7ce
--- /dev/null
+++ b/layout/base/tests/bug1663475-1-ref.html
@@ -0,0 +1,28 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted over inline with clip</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ div {
+ font: 16px/1 monospace;
+ outline: 2px solid blue;
+ caret-color: black;
+ }
+ span {
+ color: transparent;
+ }
+</style>
+<div contenteditable spellcheck="false">
+ <span>ab</span>c<span>de</span>
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable]').focus();
+ requestAnimationFrame(function() {
+ // Position the caret between "a" and "b".
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1663475-1.html b/layout/base/tests/bug1663475-1.html
new file mode 100644
index 0000000000..5e046d8c80
--- /dev/null
+++ b/layout/base/tests/bug1663475-1.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted over inline with clip</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ div {
+ font: 16px/1 monospace;
+ caret-color: black;
+ }
+ div:focus {
+ outline: 2px solid blue;
+ }
+ span {
+ /* This should only leave the "c" letter visible, but the caret should
+ still be visible when between "a" and "b" */
+ clip-path: inset(0 2ch);
+ }
+</style>
+<div contenteditable spellcheck="false">
+ <span>abcde</span>
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable]').focus();
+ requestAnimationFrame(function() {
+ // Position the caret between "a" and "b".
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1663475-2-ref.html b/layout/base/tests/bug1663475-2-ref.html
new file mode 100644
index 0000000000..25584df128
--- /dev/null
+++ b/layout/base/tests/bug1663475-2-ref.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted over inline with clip</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ span[contenteditable] {
+ font: 16px/1 monospace;
+ caret-color: black;
+ display: inline-block;
+ outline: 2px solid blue;
+ }
+ span > span {
+ color: transparent;
+ }
+</style>
+<span contenteditable spellcheck="false">
+ <span>ab</span>c<span>de</span>
+</span>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable]').focus();
+ requestAnimationFrame(function() {
+ // Position the caret between "a" and "b".
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1663475-2.html b/layout/base/tests/bug1663475-2.html
new file mode 100644
index 0000000000..aa6ddf4bff
--- /dev/null
+++ b/layout/base/tests/bug1663475-2.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Caret is correctly painted over inline with clip</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ span[contenteditable] {
+ font: 16px/1 monospace;
+ caret-color: black;
+ display: inline-block;
+ }
+ span[contenteditable]:focus {
+ outline: 2px solid blue;
+ }
+ span > span {
+ /* This should only leave the "c" letter visible, but the caret should
+ still be visible when between "a" and "b" */
+ clip-path: inset(0 2ch);
+ }
+</style>
+<span contenteditable spellcheck="false">
+ <span>abcde</span>
+</span>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable]').focus();
+ requestAnimationFrame(function() {
+ // Position the caret between "a" and "b".
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1670531-1.html b/layout/base/tests/bug1670531-1.html
new file mode 100644
index 0000000000..42fc3de818
--- /dev/null
+++ b/layout/base/tests/bug1670531-1.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Select non-editable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<span contenteditable="false">NOT EDITABLE</span>xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ // Select <span>
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1670531-2.html b/layout/base/tests/bug1670531-2.html
new file mode 100644
index 0000000000..7dfc05b06c
--- /dev/null
+++ b/layout/base/tests/bug1670531-2.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Select non-editable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<span contenteditable="false">NOT EDITABLE</span>xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move before the three x
+ for (let i = 0; i < 3; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ // Select <span>
+ synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1670531-3-ref.html b/layout/base/tests/bug1670531-3-ref.html
new file mode 100644
index 0000000000..08aaf01f08
--- /dev/null
+++ b/layout/base/tests/bug1670531-3-ref.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Select non-editable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<img src="image_rgrg-256x256.png">xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ // Select <img>
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1670531-3.html b/layout/base/tests/bug1670531-3.html
new file mode 100644
index 0000000000..aaec76ee4a
--- /dev/null
+++ b/layout/base/tests/bug1670531-3.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Select non-editable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<img contenteditable="false" src="image_rgrg-256x256.png">xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move after the two x
+ for (let i = 0; i < 2; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ // Select <img>
+ synthesizeKey("KEY_ArrowRight", { shiftKey: true });
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug1670531-4.html b/layout/base/tests/bug1670531-4.html
new file mode 100644
index 0000000000..7b7786d232
--- /dev/null
+++ b/layout/base/tests/bug1670531-4.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<title>Select non-editable content in an editor</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div:focus {
+ outline: 3px solid blue;
+ }
+</style>
+<div contenteditable="true" spellcheck="false">
+ xx<img contenteditable="false" src="image_rgrg-256x256.png">xxx
+</div>
+<script>
+SimpleTest.waitForFocus(function() {
+ document.querySelector('[contenteditable="true"]').focus();
+ requestAnimationFrame(function() {
+ // Move before the three x
+ for (let i = 0; i < 3; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ // Select <img>
+ synthesizeKey("KEY_ArrowLeft", { shiftKey: true });
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug240933-1-ref.html b/layout/base/tests/bug240933-1-ref.html
new file mode 100644
index 0000000000..778a0647b8
--- /dev/null
+++ b/layout/base/tests/bug240933-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML><html>
+<body>
+<textarea id="t" rows="4" style="-moz-appearance: none">
+
+</textarea>
+<script>
+ var t = document.getElementById("t");
+ t.selectionStart = t.selectionEnd = t.value.length;
+ t.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug240933-1.html b/layout/base/tests/bug240933-1.html
new file mode 100644
index 0000000000..771575aece
--- /dev/null
+++ b/layout/base/tests/bug240933-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<textarea id="t" rows="4" style="-moz-appearance: none"></textarea>
+<script>
+ var area = document.getElementById('t');
+ area.focus();
+
+ sendKey('RETURN'); // press Enter once
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug240933-2.html b/layout/base/tests/bug240933-2.html
new file mode 100644
index 0000000000..ea808f4d42
--- /dev/null
+++ b/layout/base/tests/bug240933-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<textarea id="t" rows="4" style="-moz-appearance: none"></textarea>
+<script>
+ var area = document.getElementById('t');
+ area.focus();
+
+ sendKey('RETURN'); // press Enter twice
+ sendKey('RETURN');
+ sendKey('BACK_SPACE'); // press Backspace once
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug369950-subframe.xml b/layout/base/tests/bug369950-subframe.xml
new file mode 100644
index 0000000000..8aed64cd4e
--- /dev/null
+++ b/layout/base/tests/bug369950-subframe.xml
@@ -0,0 +1,11 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+
+<head>
+</head>
+
+<body>
+
+<p>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 <x></y></p>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug389321-1-ref.html b/layout/base/tests/bug389321-1-ref.html
new file mode 100644
index 0000000000..f8dd9c407d
--- /dev/null
+++ b/layout/base/tests/bug389321-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<span contenteditable id="t" style="border: 1px dashed green; min-height: 2px; padding-right: 20px;"> </span></body>
+<script>
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+
+ // Focus the span to put the caret at its beginning.
+ var area = document.getElementById('t');
+ area.focus();
+
+ // Do nothing else.
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug389321-1.html b/layout/base/tests/bug389321-1.html
new file mode 100644
index 0000000000..f678865930
--- /dev/null
+++ b/layout/base/tests/bug389321-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<span contenteditable id="t" style="border: 1px dashed green; min-height: 2px; padding-right: 20px;"> </span></body>
+<script>
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+
+ // Focus the span to put the caret at its beginning.
+ var area = document.getElementById('t');
+ area.focus();
+
+ // Enter a character in the span then delete it.
+ sendChar("W");
+ sendKey("BACK_SPACE");
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug389321-2-ref.html b/layout/base/tests/bug389321-2-ref.html
new file mode 100644
index 0000000000..09adebf7e7
--- /dev/null
+++ b/layout/base/tests/bug389321-2-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML><html>
+<body>
+ <div contenteditable id="x" style="height: 30px; outline: none;"></div>
+<script>
+ var div = document.getElementById('x');
+ div.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug389321-2.html b/layout/base/tests/bug389321-2.html
new file mode 100644
index 0000000000..d878635a1d
--- /dev/null
+++ b/layout/base/tests/bug389321-2.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML><html>
+<body>
+ <div contenteditable id="x" style="height: 50px; outline: none;"></div>
+<script>
+ var div = document.getElementById('x');
+ div.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug389321-3-ref.html b/layout/base/tests/bug389321-3-ref.html
new file mode 100644
index 0000000000..387cbf25d2
--- /dev/null
+++ b/layout/base/tests/bug389321-3-ref.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML><html>
+<body>
+ <div contenteditable id="x" style="height: 30px; outline: none;">&nbsp;</div>
+<script>
+ var div = document.getElementById('x');
+ div.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug389321-3.html b/layout/base/tests/bug389321-3.html
new file mode 100644
index 0000000000..09adebf7e7
--- /dev/null
+++ b/layout/base/tests/bug389321-3.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML><html>
+<body>
+ <div contenteditable id="x" style="height: 30px; outline: none;"></div>
+<script>
+ var div = document.getElementById('x');
+ div.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug450930.xhtml b/layout/base/tests/bug450930.xhtml
new file mode 100644
index 0000000000..0981fff0a7
--- /dev/null
+++ b/layout/base/tests/bug450930.xhtml
@@ -0,0 +1,181 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=450930
+-->
+<head>
+ <title>Test for Bug 450930 (MozAfterPaint)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runNext()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=450930">Mozilla Bug 450930</a>
+<div id="display">
+ <div id="d" style="width:400px; height:200px;"></div>
+ <iframe id="iframe" style="width:400px; height:200px;"
+ src="data:text/html,&lt;div id='d'&gt;&lt;span style='margin-left:3px;'&gt;Hello&lt;/span&gt;
+ &lt;/div&gt;&lt;div style='margin-top:500px' id='d2'&gt;
+ &lt;span style='margin-left:3px;'&gt;Goodbye&lt;/span&gt;&lt;/div>"></iframe>
+ <svg:svg style="width:410px; height:210px;" id="svg">
+ <svg:foreignObject width="100%" height="100%">
+ <iframe id="iframe2" style="width:400px; height:200px;"
+ src="data:text/html,&lt;div id='d'&gt;&lt;span style='margin-left:3px;'&gt;Hello&lt;/span&gt;
+ &lt;/div&gt;&lt;div style='margin-top:500px' id='d2'&gt;
+ &lt;span style='margin-left:3px;'&gt;Goodbye&lt;/span&gt;&lt;/div>"></iframe>
+ </svg:foreignObject>
+ </svg:svg>
+</div>
+<div id="content" style="display: none">
+</div>
+
+
+<pre id="test">
+<script class="testbody" type="text/javascript"><![CDATA[
+
+function flash(doc, name) {
+ var d = doc.getElementById(name);
+ d.style.backgroundColor = d.style.backgroundColor == "blue" ? "yellow" : "blue";
+ // Now flush out style changes in that document, since our event listeners
+ // seem to assume that things will work that way.
+ d.getBoundingClientRect();
+}
+
+function le(v1, v2, s) {
+ window.opener.ok(v1 <= v2, s + " (" + v1 + "," + v2 + ")");
+}
+
+function checkContains(r1, r2, s) {
+ le(Math.round(r1.left), Math.round(r2.left), "Left edges out" + s);
+ le(Math.round(r2.right), Math.round(r1.right), "Right edges out" + s);
+ le(Math.round(r1.top), Math.round(r2.top), "Top edges out" + s);
+ le(Math.round(r2.bottom), Math.round(r1.bottom), "Bottom edges out" + s);
+}
+
+function isRect(r1, r2) {
+ return (Math.abs(r1.left - r2.left) <= 1 ||
+ Math.abs(r1.right - r2.right) <= 1 ||
+ Math.abs(r1.top - r2.top) <= 1 ||
+ Math.abs(r1.bottom - r2.bottom) <= 1);
+}
+
+function isRectInList(r, list) {
+ for (var i = 0; i < list.length; ++i) {
+ if (isRect(r, list[i]))
+ return true;
+ }
+ return false;
+}
+
+function doesRectContain(r1, r2) {
+ return Math.floor(r1.left) <= r2.left && r2.right <= Math.ceil(r1.right) &&
+ Math.floor(r1.top) <= r2.top && r2.bottom <= Math.ceil(r1.bottom);
+}
+
+function rectToString(r) {
+ return "(" + r.left + "," + r.top + "," + r.right + "," + r.bottom + ")";
+}
+
+function doesRectContainListElement(r, list) {
+ dump("Incoming rect: " + rectToString(r) + "\n");
+ for (var i = 0; i < list.length; ++i) {
+ dump("List rect " + i + ": " + rectToString(list[i]));
+ if (doesRectContain(r, list[i])) {
+ dump(" FOUND\n");
+ return true;
+ }
+ dump("\n");
+ }
+ dump("NOT FOUND\n");
+ return false;
+}
+
+function checkGotSubdoc(list, container) {
+ var r = container.getBoundingClientRect();
+ return doesRectContainListElement(r, list);
+}
+
+function runTest1() {
+ // test basic functionality
+ var iterations = 0;
+ var foundExactRect = false;
+
+ function listener(event) {
+ var r = SpecialPowers.wrap(event).boundingClientRect;
+ var bounds = document.getElementById('d').getBoundingClientRect();
+ checkContains(r, bounds, "");
+ if (isRectInList(bounds, SpecialPowers.wrap(event).clientRects)) {
+ foundExactRect = true;
+ }
+ window.removeEventListener("MozAfterPaint", listener);
+ ++iterations;
+ if (iterations < 4) {
+ setTimeout(triggerPaint, 100);
+ } else {
+ window.opener.ok(foundExactRect, "Found exact rect");
+ runNext();
+ }
+ }
+
+ function triggerPaint() {
+ window.addEventListener("MozAfterPaint", listener);
+ flash(document, 'd');
+ window.opener.ok(true, "trigger test1 paint");
+ }
+ triggerPaint();
+}
+
+function runTest2(frameID, containerID) {
+ // test reporting of painting in subdocuments
+ var fired = 0;
+ var gotSubdocPrivileged = false;
+ var iframe = document.getElementById(frameID);
+ var container = document.getElementById(containerID);
+
+ function listener(event) {
+ if (checkGotSubdoc(SpecialPowers.wrap(event).clientRects, container))
+ gotSubdocPrivileged = true;
+ if (SpecialPowers.wrap(event).clientRects.length > 0) {
+ if (++fired == 1)
+ setTimeout(check, 100);
+ }
+ }
+
+ function check() {
+ window.opener.is(fired, 1, "Wrong event count (" + frameID + ")");
+ window.opener.ok(gotSubdocPrivileged, "Didn't get subdoc invalidation while we were privileged (" + frameID + ")");
+ window.removeEventListener("MozAfterPaint", listener);
+ runNext();
+ }
+
+ function triggerPaint() {
+ window.addEventListener("MozAfterPaint", listener);
+ document.body.offsetTop;
+ flash(iframe.contentDocument, 'd');
+ }
+ triggerPaint();
+}
+
+var test = 0;
+var tests = [runTest1,
+ function() { runTest2("iframe", "iframe") },
+ function() { runTest2("iframe2", "svg") }];
+function runNext() {
+ if (SpecialPowers.DOMWindowUtils.isMozAfterPaintPending) {
+ // Wait until there are no pending paints before trying to run tests
+ setTimeout(runNext, 100);
+ return;
+ }
+ if (test < tests.length) {
+ ++test;
+ tests[test - 1]();
+ } else {
+ window.opener.finishTests();
+ }
+}
+
+
+]]></script>
+</pre>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug482484-ref.html b/layout/base/tests/bug482484-ref.html
new file mode 100644
index 0000000000..c8b8c2bab3
--- /dev/null
+++ b/layout/base/tests/bug482484-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML><html><head></head>
+<body>
+<div contentEditable="true" id="div" spellcheck="false"><p id="p">ABC</p></div>
+<script>
+ // Position the caret after the "A"
+ var div = document.getElementById('div');
+ var p = document.getElementById('p');
+ div.focus();
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ range.setStart(p.firstChild, 1)
+ range.setEnd(p.firstChild, 1);
+ sel.addRange(range);
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug482484.html b/layout/base/tests/bug482484.html
new file mode 100644
index 0000000000..1f05124a2b
--- /dev/null
+++ b/layout/base/tests/bug482484.html
@@ -0,0 +1,22 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div contentEditable="true" id="div" spellcheck="false"><p id="p">BC</p></div>
+<script>
+ // Position the caret before the "B"
+ var div = document.getElementById('div');
+ div.focus();
+ var p = document.getElementById('p');
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ range.setStart(p.firstChild, 0)
+ range.setEnd(p.firstChild, 0);
+ sel.addRange(range);
+
+ sendKey('UP'); // move UP
+ sendChar('A'); // insert "A"
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug503399-ref.html b/layout/base/tests/bug503399-ref.html
new file mode 100644
index 0000000000..2f2af9c31d
--- /dev/null
+++ b/layout/base/tests/bug503399-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug 503399</title>
+ <style type="text/css">
+ html, body {
+ color: black;
+ background-color: white;
+ font: 16px monospace;
+ }
+ p {
+ text-align: justify;
+ max-width: 180px;
+ height: 1em;
+ overflow: hidden;
+ position: relative;
+ }
+ span {
+ display: inline-block;
+ border-left: 1px solid black;
+ position: absolute;
+ height: 100%;
+ }
+ </style>
+ <script>
+ var done = false;
+ function runTest(p) {
+ if (done)
+ return;
+ try {
+ getSelection().collapse(p.childNodes[0], 14);
+ } catch (e) {}
+ document.documentElement.removeAttribute('class');
+ done = true;
+ }
+ </script>
+</head>
+<body onload="var p = document.getElementsByTagName('p')[0]; p.focus(); setTimeout(function(){runTest(p)},1000)">
+ <p onfocus="runTest(this)" contentEditable="true">&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<span></span> &nbsp;&nbsp; &nbsp;&nbsp;</p>
+</body>
+</html>
diff --git a/layout/base/tests/bug503399.html b/layout/base/tests/bug503399.html
new file mode 100644
index 0000000000..96288b1702
--- /dev/null
+++ b/layout/base/tests/bug503399.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+ <title>Testcase for bug 503399</title>
+ <style type="text/css">
+ html, body {
+ color: black;
+ background-color: white;
+ font: 16px monospace;
+ }
+ p {
+ text-align: justify;
+ max-width: 180px;
+ height: 1em;
+ overflow: hidden;
+ position: relative;
+ }
+ span {
+ display: inline-block;
+ position: absolute;
+ height: 100%;
+ }
+ </style>
+ <script>
+ var done = false;
+ function runTest(p) {
+ if (done)
+ return;
+ try {
+ getSelection().collapse(p.childNodes[0], 14);
+ } catch (e) {}
+ document.documentElement.removeAttribute('class');
+ done = true;
+ }
+ </script>
+</head>
+<body onload="var p = document.getElementsByTagName('p')[0]; p.focus(); setTimeout(function(){runTest(p)},1000)">
+ <p onfocus="runTest(this)" contentEditable="true">&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;<span></span> &nbsp;&nbsp; &nbsp;&nbsp;</p>
+</body>
+</html>
diff --git a/layout/base/tests/bug512295-1-ref.html b/layout/base/tests/bug512295-1-ref.html
new file mode 100644
index 0000000000..8c33e9fa7c
--- /dev/null
+++ b/layout/base/tests/bug512295-1-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML><html class="reftest-wait"><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div contenteditable="true">
+<p id="p">A B CD EFG<br>
+ 1234567890</p>
+</div>
+x
+<script>
+ // Position the caret at the end of the P element
+ var p = document.getElementById('p');
+ var div = p.parentNode;
+ div.focus();
+ var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.import(
+ "resource://testing-common/AsyncSpellCheckTestHelper.jsm"
+ );
+ maybeOnSpellCheck(div, function () {
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ range.setStart(p, p.childNodes.length);
+ range.setEnd(p, p.childNodes.length);
+ sel.addRange(range);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug512295-1.html b/layout/base/tests/bug512295-1.html
new file mode 100644
index 0000000000..d475b36099
--- /dev/null
+++ b/layout/base/tests/bug512295-1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML><html class="reftest-wait"><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<div contenteditable="true">
+<p id="p">A B CD EFG<br>
+ 1234567890</p>
+</div>
+x
+<script>
+ // Position the caret after "A"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var p = document.getElementById('p');
+ var t = p.firstChild;
+ range.setStart(t, 1);
+ range.setEnd(t, 1);
+ sel.addRange(range);
+ p.parentNode.focus();
+
+ var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.import(
+ "resource://testing-common/AsyncSpellCheckTestHelper.jsm"
+ );
+ maybeOnSpellCheck(p.parentNode, function () {
+ sendKey('DOWN'); // now after "1"
+ sendKey('DOWN'); // now make sure we get to the end
+ sendKey('DOWN'); // now make sure we get to the end
+ sendKey('DOWN'); // now make sure we get to the end
+ sendKey('DOWN'); // now make sure we get to the end
+ sendKey('DOWN'); // now make sure we get to the end
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug512295-2-ref.html b/layout/base/tests/bug512295-2-ref.html
new file mode 100644
index 0000000000..c32a30fa59
--- /dev/null
+++ b/layout/base/tests/bug512295-2-ref.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML><html class="reftest-wait"><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+x
+<div contenteditable="true">
+<p id="p">A B CD EFG<br>
+ 1234567890</p>
+</div>
+<script>
+ // Position the caret before the "A"
+ var p = document.getElementById('p');
+ var div = p.parentNode;
+ div.focus();
+ var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.import(
+ "resource://testing-common/AsyncSpellCheckTestHelper.jsm"
+ );
+ maybeOnSpellCheck(div, function () {
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ range.setStart(p.firstChild, 0);
+ range.setEnd(p.firstChild, 0);
+ sel.addRange(range);
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug512295-2.html b/layout/base/tests/bug512295-2.html
new file mode 100644
index 0000000000..9baa8bcdb8
--- /dev/null
+++ b/layout/base/tests/bug512295-2.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML><html class="reftest-wait"><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+x
+<div contenteditable="true">
+<p id="p">A B CD EFG<br>
+ 1234567890</p>
+</div>
+<script>
+ // Position the caret after "A"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var p = document.getElementById('p');
+ var t = p.firstChild;
+ range.setStart(t, 1);
+ range.setEnd(t, 1);
+ sel.addRange(range);
+ p.parentNode.focus();
+
+ var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.import(
+ "resource://testing-common/AsyncSpellCheckTestHelper.jsm"
+ );
+ maybeOnSpellCheck(p.parentNode, function () {
+ sendKey('DOWN'); // now after "1"
+ sendKey('DOWN'); // now below the P element
+ sendKey('UP'); // now before the "1"
+ sendKey('UP'); // now before the "A"
+ sendKey('UP'); // now before the "A"
+ sendKey('UP'); // now before the "A"
+ document.documentElement.classList.remove("reftest-wait");
+ });
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug558663.html b/layout/base/tests/bug558663.html
new file mode 100644
index 0000000000..4c5f81153a
--- /dev/null
+++ b/layout/base/tests/bug558663.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=558663
+-->
+<head>
+ <title>Test for Bug 558663</title>
+</head>
+<body>
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=558663">Mozilla Bug 558663</a></p>
+
+ <!-- 20x20 of red -->
+<iframe id="iframe" srcdoc="<img id='image' border='0' src='data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC'>"></iframe>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 558663 **/
+var ok = parent.ok;
+var SimpleTest = parent.SimpleTest;
+var compareSnapshots = parent.compareSnapshots;
+var snapshotWindow = parent.snapshotWindow;
+var synthesizeMouse = parent.synthesizeMouse;
+
+window.addEventListener("load", runTest);
+
+function checkSnapshots(s1, s2, shouldBeEqual, testName) {
+ var res = compareSnapshots(s1, s2, shouldBeEqual);
+ if (res[0]) {
+ ok(true, testName + " snapshots compare correctly");
+ } else {
+ ok(false, testName + " snapshots compare incorrectly. snapshot 1: " +
+ res[1] + " snapshot 2: " + res[2]);
+ }
+}
+
+function runTest() {
+ var contentDocument = document.getElementById("iframe").contentDocument;
+ contentDocument.designMode = "on";
+ contentDocument.execCommand("enableObjectResizing", false, true);
+
+ // The editor requires the event loop to spin after you turn on design mode
+ // before it takes effect.
+ setTimeout(continueTest, 100);
+}
+
+function continueTest() {
+ var win = document.getElementById("iframe").contentWindow;
+ var doc = win.document;
+ var image = doc.getElementById("image");
+
+ // We want to test that clicking on the image and then clicking on one of the
+ // draggers doesn't make the draggers disappear.
+
+ // clean snapshot
+ var before = snapshotWindow(win);
+
+ // click to get the draggers
+ synthesizeMouse(image, 1, 1, {type: "mousedown"}, win);
+ synthesizeMouse(image, 1, 1, {type: "mouseup"}, win);
+
+ // mouse over a dragger will change its color, so move the mouse away
+ synthesizeMouse(doc.documentElement, 50, 50, {type: "mousemove"}, win);
+
+ // snapshot with hopefully draggers
+ var middle = snapshotWindow(win);
+
+ // clicking on the top left dragger shouldn't change anything
+ synthesizeMouse(image, 1, 1, {type: "mousedown"}, win);
+ synthesizeMouse(image, 1, 1, {type: "mouseup"}, win);
+
+ // mouse over a dragger will change its color, so move the mouse away
+ synthesizeMouse(doc.documentElement, 50, 50, {type: "mousemove"}, win);
+
+ // snapshot with hopefully draggers again
+ var middle2 = snapshotWindow(win);
+
+ // click outside the image (but inside the document) to unselect it
+ synthesizeMouse(doc.documentElement, 50, 50, {type: "mousedown"}, win);
+ synthesizeMouse(doc.documentElement, 50, 50, {type: "mouseup"}, win);
+
+ // and then click outside the document so we don't draw a caret
+ synthesizeMouse(document.documentElement, 1, 1, {type: "mousedown"}, window);
+ synthesizeMouse(document.documentElement, 1, 1, {type: "mouseup"}, window);
+
+ // hopefully clean snapshot
+ var end = snapshotWindow(win);
+
+ // before == end && middle == middle2 && before/end != middle/middle2
+ checkSnapshots(before, end, true, "before and after should be the same")
+ checkSnapshots(middle, middle2, true, "middle two should be the same");
+ checkSnapshots(before, middle, false, "before and middle should not be the same");
+ checkSnapshots(before, middle2, false, "before and middle2 should not be the same");
+ checkSnapshots(middle, end, false, "middle and end should not be the same");
+ checkSnapshots(middle2, end, false, "middle2 and end should not be the same");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug583889_inner1.html b/layout/base/tests/bug583889_inner1.html
new file mode 100644
index 0000000000..7a223569d2
--- /dev/null
+++ b/layout/base/tests/bug583889_inner1.html
@@ -0,0 +1,64 @@
+<html>
+<body>
+<iframe id="inner" style="height: 10px; width: 10px"></iframe>
+<div style="width: 1000px; height: 1000px"></div>
+<script type="application/javascript">
+function grabEventAndGo(event) {
+ gen.next(event);
+}
+
+function waitAsync() {
+ setTimeout(function() { gen.next() }, 0);
+}
+
+function postPos() {
+ parent.postMessage(JSON.stringify({ top: document.body.scrollTop,
+ left: document.body.scrollLeft }),
+ "*");
+}
+
+function* runTest() {
+ var inner = document.getElementById("inner");
+ window.onload = grabEventAndGo;
+ // Wait for onLoad event.
+ yield;
+
+ document.body.scrollTop = 300;
+ document.body.scrollLeft = 300;
+
+ postPos();
+
+ inner.src = "bug583889_inner2.html#id1";
+ inner.onload = grabEventAndGo;
+ // Let parent process sent message.
+ // Wait for onLoad event from 'inner' iframe.
+ yield;
+
+ postPos();
+
+ inner.onload = null;
+ dump("hi\n");
+ inner.contentWindow.location = "bug583889_inner2.html#id2"
+ waitAsync();
+ // Let parent process sent message.
+ // Let 'inner' iframe update itself.
+ yield;
+
+ postPos();
+
+ inner.contentWindow.location.hash = "#id3"
+ waitAsync();
+ // Let parent process sent message.
+ // Let 'inner' iframe update itself.
+ yield;
+
+ postPos();
+
+ parent.postMessage("done", "*");
+}
+
+var gen = runTest();
+gen.next();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug583889_inner2.html b/layout/base/tests/bug583889_inner2.html
new file mode 100644
index 0000000000..ce63f54cfd
--- /dev/null
+++ b/layout/base/tests/bug583889_inner2.html
@@ -0,0 +1,5 @@
+<body>
+<a id="id1">link 1</a>
+<a id="id2">link 2</a>
+<a id="id3">link 3</a>
+</body>
diff --git a/layout/base/tests/bug585922-ref.html b/layout/base/tests/bug585922-ref.html
new file mode 100644
index 0000000000..8009314c86
--- /dev/null
+++ b/layout/base/tests/bug585922-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="doTest()">
+ <input type=text style="-moz-appearance: none">
+ <script>
+ function doTest() {
+ var d = document.querySelector("input");
+ d.value = "b";
+ d.focus();
+ var editor = SpecialPowers.wrap(d).editor;
+ var sel = editor.selection;
+ var t = editor.rootElement.firstChild;
+ sel.collapse(t, 1); // put the caret at the end of the textbox
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug585922.html b/layout/base/tests/bug585922.html
new file mode 100644
index 0000000000..4f4eaeb6b9
--- /dev/null
+++ b/layout/base/tests/bug585922.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="doTest()">
+ <input type=text style="-moz-appearance: none">
+ <script>
+ function doTest() {
+ function enableCaret(aEnable) {
+ var selCon = editor.selectionController;
+ selCon.setCaretEnabled(aEnable);
+ }
+
+ var d = document.querySelector("input");
+ d.value = "a";
+ d.focus();
+ var editor = SpecialPowers.wrap(d).editor;
+ var sel = editor.selection;
+ var t = editor.rootElement.firstChild;
+ sel.collapse(t, 1); // put the caret at the end of the div
+ setTimeout(function() {
+ enableCaret(false);enableCaret(true);// force a caret display
+ enableCaret(false); // hide the caret
+ t.replaceData(0, 1, "b"); // replace the text node data
+ // at this point, the selection is collapsed to offset 0
+ synthesizeQuerySelectedText(); // call nsCaret::GetGeometry
+ sel.collapse(t, 1); // put the caret at the end again
+ enableCaret(true); // show the caret again
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug597519-1-ref.html b/layout/base/tests/bug597519-1-ref.html
new file mode 100644
index 0000000000..e11eb0c967
--- /dev/null
+++ b/layout/base/tests/bug597519-1-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML><html><head>
+</head>
+<body>
+<textarea spellcheck="false" style="-moz-appearance: none">ab
+</textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+ t.selectionStart = t.selectionEnd = t.value.length;
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug597519-1.html b/layout/base/tests/bug597519-1.html
new file mode 100644
index 0000000000..2fcddce515
--- /dev/null
+++ b/layout/base/tests/bug597519-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<textarea maxlength="3" spellcheck="false" style="-moz-appearance: none"></textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+
+ sendString("ab");
+ synthesizeKey("KEY_Enter");
+ sendString("c");
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-1-ref.html b/layout/base/tests/bug602141-1-ref.html
new file mode 100644
index 0000000000..64cbd58c3d
--- /dev/null
+++ b/layout/base/tests/bug602141-1-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML><html><head>
+</head>
+<body>
+<span contenteditable="true" spellcheck="false">navigable__</span><span id="x" contenteditable="true" spellcheck="false">navigable|unnavigable</span><br />
+<script>
+ // Position the caret after "u"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 11);
+ range.setEnd(t, 11);
+ sel.addRange(range);
+ x.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-1.html b/layout/base/tests/bug602141-1.html
new file mode 100644
index 0000000000..4a3d3614ee
--- /dev/null
+++ b/layout/base/tests/bug602141-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<span contenteditable="true" spellcheck="false">navigable__</span><span id="x" contenteditable="true" spellcheck="false">navigable|unnavigable</span><br />
+<script>
+ // Position the caret after "|"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 10);
+ range.setEnd(t, 10);
+ sel.addRange(range);
+ x.focus();
+
+ sendKey('RIGHT'); // Try to move the caret one position to the right
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-2-ref.html b/layout/base/tests/bug602141-2-ref.html
new file mode 100644
index 0000000000..f54518a024
--- /dev/null
+++ b/layout/base/tests/bug602141-2-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML><html><head>
+</head>
+<body>
+<span id="x" contenteditable="true" spellcheck="false">navigable__|unnavigable</span><br />
+<script>
+ // Position the caret after "u"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 13);
+ range.setEnd(t, 13);
+ sel.addRange(range);
+ x.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-2.html b/layout/base/tests/bug602141-2.html
new file mode 100644
index 0000000000..e86c906f3c
--- /dev/null
+++ b/layout/base/tests/bug602141-2.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<span id="x" contenteditable="true" spellcheck="false">navigable__|</span><br />
+<script>
+ document.getElementById('x').appendChild(document.createTextNode('unnavigable'));
+
+ // Position the caret after "|"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 12);
+ range.setEnd(t, 12);
+ sel.addRange(range);
+ x.focus();
+
+ sendKey('RIGHT'); // Try to move the caret one position to the right
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-3-ref.html b/layout/base/tests/bug602141-3-ref.html
new file mode 100644
index 0000000000..8d39318ccd
--- /dev/null
+++ b/layout/base/tests/bug602141-3-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML><html><head>
+</head>
+<body>
+noteditable<span id="x" contenteditable="true" spellcheck="false">navigable|unnavigable</span><br />
+<script>
+ // Position the caret after "u"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 11);
+ range.setEnd(t, 11);
+ sel.addRange(range);
+ x.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-3.html b/layout/base/tests/bug602141-3.html
new file mode 100644
index 0000000000..fa153a6079
--- /dev/null
+++ b/layout/base/tests/bug602141-3.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+noteditable<span id="x" contenteditable="true" spellcheck="false">navigable|unnavigable</span><br />
+<script>
+ // Position the caret after "|"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 10);
+ range.setEnd(t, 10);
+ sel.addRange(range);
+ x.focus();
+
+ sendKey('RIGHT'); // Try to move the caret one position to the right
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-4-ref.html b/layout/base/tests/bug602141-4-ref.html
new file mode 100644
index 0000000000..c67986c5f0
--- /dev/null
+++ b/layout/base/tests/bug602141-4-ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE HTML><html><head>
+</head>
+<body>
+<span>not editable</span><span id="x" contenteditable="true" spellcheck="false">navigable|unnavigable</span>
+<script>
+ // Position the caret after "u"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 11);
+ range.setEnd(t, 11);
+ sel.addRange(range);
+ x.focus();
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug602141-4.html b/layout/base/tests/bug602141-4.html
new file mode 100644
index 0000000000..b2c8185ab8
--- /dev/null
+++ b/layout/base/tests/bug602141-4.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<span>not editable</span><span id="x" contenteditable="true" spellcheck="false">navigable|unnavigable</span>
+<script>
+ // Position the caret after "|"
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ var range = document.createRange();
+ var x = document.getElementById('x');
+ var t = x.firstChild;
+ range.setStart(t, 10);
+ range.setEnd(t, 10);
+ sel.addRange(range);
+ x.focus();
+
+ sendKey('RIGHT'); // Try to move the caret one position to the right
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug612271-1.html b/layout/base/tests/bug612271-1.html
new file mode 100644
index 0000000000..226d7e7d07
--- /dev/null
+++ b/layout/base/tests/bug612271-1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+ <textarea id="target" style="height: 100px; -moz-appearance: none" spellcheck="false"
+ onkeydown="this.style.display='block';this.style.height='200px';">foo</textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+ t.selectionStart = t.selectionEnd = t.value.length;
+ sendKey('RETURN');
+ document.body.appendChild(document.createTextNode(t.selectionStart + " - " + t.selectionEnd));
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug612271-2.html b/layout/base/tests/bug612271-2.html
new file mode 100644
index 0000000000..8767468131
--- /dev/null
+++ b/layout/base/tests/bug612271-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+ <textarea id="target" style="height: 100px; -moz-appearance: none" spellcheck="false"
+ onkeypress="this.style.display='block';this.style.height='200px';">foo</textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+ t.selectionStart = t.selectionEnd = t.value.length;
+ sendKey('RETURN');
+ document.body.appendChild(document.createTextNode(t.selectionStart + " - " + t.selectionEnd));
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug612271-3.html b/layout/base/tests/bug612271-3.html
new file mode 100644
index 0000000000..9f267c2824
--- /dev/null
+++ b/layout/base/tests/bug612271-3.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+ <textarea id="target" style="height: 100px; -moz-appearance: none" spellcheck="false"
+ onkeyup="this.style.display='block';this.style.height='200px';">foo</textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+ t.selectionStart = t.selectionEnd = t.value.length;
+ sendKey('RETURN');
+ document.body.appendChild(document.createTextNode(t.selectionStart + " - " + t.selectionEnd));
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug612271-ref.html b/layout/base/tests/bug612271-ref.html
new file mode 100644
index 0000000000..0d3e96b98a
--- /dev/null
+++ b/layout/base/tests/bug612271-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript">
+ function loaded() {
+ var t = document.querySelector("textarea");
+ t.focus();
+ t.selectionStart = t.selectionEnd = 4;
+ }
+ </script>
+</head>
+<body onload="loaded()">
+ <textarea style="height: 200px; display: block; -moz-appearance: none" spellcheck="false"
+ >foo
+</textarea>
+ 4 - 4
+</body>
+</html>
diff --git a/layout/base/tests/bug613433-1.html b/layout/base/tests/bug613433-1.html
new file mode 100644
index 0000000000..837dff89da
--- /dev/null
+++ b/layout/base/tests/bug613433-1.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <style>
+ div {
+ min-height: 36px;
+ overflow-x: auto;
+ }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function test() {
+ document.querySelector("div").focus();
+ // type a character, then press backspace to delete it
+ sendChar("X");
+ sendKey("BACK_SPACE");
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="test()">
+ <div id="div1" contenteditable></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug613433-2.html b/layout/base/tests/bug613433-2.html
new file mode 100644
index 0000000000..84d55e7be5
--- /dev/null
+++ b/layout/base/tests/bug613433-2.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <style>
+ div {
+ min-height: 36px;
+ overflow-y: auto;
+ }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function test() {
+ document.querySelector("div").focus();
+ // type a character, then press backspace to delete it
+ sendChar("X");
+ sendKey("BACK_SPACE");
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="test()">
+ <div id="div1" contenteditable></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug613433-3.html b/layout/base/tests/bug613433-3.html
new file mode 100644
index 0000000000..aa7b2853a4
--- /dev/null
+++ b/layout/base/tests/bug613433-3.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <style>
+ div {
+ min-height: 36px;
+ overflow: auto;
+ }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function test() {
+ document.querySelector("div").focus();
+ // type a character, then press backspace to delete it
+ sendChar("X");
+ sendKey("BACK_SPACE");
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="test()">
+ <div id="div1" contenteditable></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug613433-ref.html b/layout/base/tests/bug613433-ref.html
new file mode 100644
index 0000000000..f4a2ab3b6f
--- /dev/null
+++ b/layout/base/tests/bug613433-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <style>
+ div {
+ min-height: 36px;
+ }
+ </style>
+ <script>
+ function test() {
+ document.querySelector("div").focus();
+ }
+ function focusTriggered() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </head>
+ <body onload="test()">
+ <div contenteditable onfocus="focusTriggered()"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/bug613807-1-ref.html b/layout/base/tests/bug613807-1-ref.html
new file mode 100644
index 0000000000..b47a572ea7
--- /dev/null
+++ b/layout/base/tests/bug613807-1-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body onload="document.querySelector('textarea').focus()">
+<textarea id="t" rows="4" style="-moz-appearance: none"></textarea>
+</body>
+</html>
diff --git a/layout/base/tests/bug613807-1.html b/layout/base/tests/bug613807-1.html
new file mode 100644
index 0000000000..79592fed7a
--- /dev/null
+++ b/layout/base/tests/bug613807-1.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<textarea id="t" rows="4" style="-moz-appearance: none"></textarea>
+<script>
+ if (typeof(addLoadEvent) == 'undefined') {
+ _newCallStack = function(path) {
+ var rval = function () {
+ var callStack = arguments.callee.callStack;
+ for (var i = 0; i < callStack.length; i++) {
+ if (callStack[i].apply(this, arguments) === false) {
+ break;
+ }
+ }
+ try {
+ this[path] = null;
+ } catch (e) {
+ // pass
+ }
+ };
+ rval.callStack = [];
+ return rval;
+ };
+ function addLoadEvent(func) {
+ var existing = window.onload;
+ var regfunc = existing;
+ if (!(typeof(existing) == 'function'
+ && typeof(existing.callStack) == "object"
+ && existing.callStack !== null)) {
+ regfunc = _newCallStack("onload");
+ if (typeof(existing) == 'function') {
+ regfunc.callStack.push(existing);
+ }
+ window.onload = regfunc;
+ }
+ regfunc.callStack.push(func);
+ };
+ }
+
+ addLoadEvent(function() {
+ var area = document.getElementById('t');
+ area.focus();
+
+ var domWindowUtils = SpecialPowers.getDOMWindowUtils(window);
+
+ // input raw characters
+ synthesizeCompositionChange(
+ { composition:
+ { string: "\u306D",
+ clauses: [
+ { length: 1, attr: COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ caret: { start: 1, length: 0 }
+ });
+ synthesizeCompositionChange(
+ { composition:
+ { string: "\u306D\u3053",
+ clauses: [
+ { length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE }
+ ]
+ },
+ caret: { start: 2, length: 0 }
+ });
+
+ // convert
+ synthesizeCompositionChange(
+ { composition:
+ { string: "\u732B",
+ clauses: [
+ { length: 1, attr: COMPOSITION_ATTR_SELECTED_CLAUSE }
+ ]
+ },
+ caret: { start: 1, length: 0 }
+ });
+
+ // commit
+ synthesizeComposition({ type: "compositioncommitasis" });
+
+ document.body.clientWidth;
+
+ // undo
+ synthesizeKey("Z", {accelKey: true});
+ });
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug632215-1.html b/layout/base/tests/bug632215-1.html
new file mode 100644
index 0000000000..c64330a6de
--- /dev/null
+++ b/layout/base/tests/bug632215-1.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body>
+ <iframe srcdoc="<body spellcheck=false></body>"></iframe>
+ <script>
+ onload = function() {
+ var i = document.querySelector("iframe");
+ var d = i.contentDocument;
+ var w = i.contentWindow;
+ var s = w.getSelection();
+ i.focus();
+ d.body.contentEditable = true;
+ d.body.contentEditable = false;
+ d.designMode = "off";
+ d.designMode = "on";
+ d.body.focus();
+ sendString("x");
+ s.collapse(d.body.firstChild, 1);
+ sendString("x");
+ setTimeout(function() {
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ };
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug632215-2.html b/layout/base/tests/bug632215-2.html
new file mode 100644
index 0000000000..02b0acd953
--- /dev/null
+++ b/layout/base/tests/bug632215-2.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body>
+ <iframe srcdoc="<body contenteditable spellcheck=false></body>"></iframe>
+ <script>
+ onload = function() {
+ var i = document.querySelector("iframe");
+ var d = i.contentDocument;
+ var w = i.contentWindow;
+ var s = w.getSelection();
+ i.focus();
+ d.body.contentEditable = false;
+ d.designMode = "off";
+ d.designMode = "on";
+ d.body.focus();
+ sendString("x");
+ s.collapse(d.body.firstChild, 1);
+ sendString("x");
+ setTimeout(function() {
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ };
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug632215-ref.html b/layout/base/tests/bug632215-ref.html
new file mode 100644
index 0000000000..b0e4bcff7f
--- /dev/null
+++ b/layout/base/tests/bug632215-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <iframe srcdoc="<body spellcheck=false>xx</body>"></iframe>
+ <script>
+ onload = function() {
+ var i = document.querySelector("iframe");
+ var d = i.contentDocument;
+ var w = i.contentWindow;
+ d.designMode = "on";
+ i.focus();
+ d.body.focus();
+ w.getSelection().collapse(d.body.firstChild, 2);
+ };
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug633044-1-ref.html b/layout/base/tests/bug633044-1-ref.html
new file mode 100644
index 0000000000..330d5777f9
--- /dev/null
+++ b/layout/base/tests/bug633044-1-ref.html
@@ -0,0 +1,16 @@
+<html>
+ <head>
+ <script>
+ onload = function() {
+ var el;
+ while (el = document.querySelector("br")) {
+ el.remove();
+ }
+ focus();
+ document.body.focus();
+ getSelection().collapse(document.body.firstChild, 0);
+ }
+ </script>
+</head>
+
+<body style="white-space:pre-wrap;" contenteditable></body></html>
diff --git a/layout/base/tests/bug633044-1.html b/layout/base/tests/bug633044-1.html
new file mode 100644
index 0000000000..7add9020de
--- /dev/null
+++ b/layout/base/tests/bug633044-1.html
@@ -0,0 +1,24 @@
+<html>
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ onload = function() {
+ var el;
+ while (el = document.querySelector("br")) {
+ el.remove();
+ }
+ focus();
+ document.body.focus();
+ getSelection().collapse(document.body.firstChild, 0);
+
+ var range = window.getSelection().getRangeAt(0);
+ var el = document.createTextNode(" ");
+ range.insertNode(el);
+ el.remove();
+
+ synthesizeKey("KEY_ArrowUp");
+ }
+ </script>
+</head>
+
+<body style="white-space:pre-wrap;" contenteditable></body></html>
diff --git a/layout/base/tests/bug634406-1-ref.html b/layout/base/tests/bug634406-1-ref.html
new file mode 100644
index 0000000000..87b42a9ede
--- /dev/null
+++ b/layout/base/tests/bug634406-1-ref.html
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML><html>
+<body>
+<textarea spellcheck="false" style="-moz-appearance: none">ab</textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+ t.selectionStart = t.selectionEnd = t.value.length;
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug634406-1.html b/layout/base/tests/bug634406-1.html
new file mode 100644
index 0000000000..1b13a4c636
--- /dev/null
+++ b/layout/base/tests/bug634406-1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML><html><head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<textarea spellcheck="false" style="-moz-appearance: none"></textarea>
+<script>
+ var t = document.querySelector("textarea");
+ t.focus();
+
+ sendString("a");
+ synthesizeKey("A", {accelKey: true});
+ synthesizeKey("KEY_ArrowRight");
+ sendString("b");
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug644428-1-ref.html b/layout/base/tests/bug644428-1-ref.html
new file mode 100644
index 0000000000..9a771db851
--- /dev/null
+++ b/layout/base/tests/bug644428-1-ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="setupCaret()">
+ <div contenteditable>a</div>
+ <script>
+ function setupCaret() {
+ var div = document.querySelector("div");
+ div.focus();
+ var sel = window.getSelection();
+ sel.collapse(div, 1);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug644428-1.html b/layout/base/tests/bug644428-1.html
new file mode 100644
index 0000000000..d304beb97f
--- /dev/null
+++ b/layout/base/tests/bug644428-1.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="setupCaret()" spellcheck="false">
+ <div contenteditable>a<span>b</span>c </div>
+ <script>
+ function setupCaret() {
+ var div = document.querySelector("div");
+ div.focus();
+ var sel = window.getSelection();
+ sel.collapse(div, 3);
+ synthesizeKey("KEY_Backspace");
+ synthesizeKey("KEY_Backspace");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug646382-1-ref.html b/layout/base/tests/bug646382-1-ref.html
new file mode 100644
index 0000000000..e9d37339e4
--- /dev/null
+++ b/layout/base/tests/bug646382-1-ref.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ </head>
+ <body onload="start()">
+ <textarea onfocus="done()"
+ style="-moz-appearance: none;
+ unicode-bidi: bidi-override;">س</textarea>
+ <script>
+ var textarea = document.querySelector("textarea");
+ function start() {
+ textarea.focus();
+ textarea.selectionStart = 1; // place caret after the letter
+ textarea.selectionEnd = 1;
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug646382-1.html b/layout/base/tests/bug646382-1.html
new file mode 100644
index 0000000000..949d5c650a
--- /dev/null
+++ b/layout/base/tests/bug646382-1.html
@@ -0,0 +1,22 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <textarea onfocus="typeIntoMe()" style="-moz-appearance: none"></textarea>
+ <script>
+ function start() {
+ document.querySelector("textarea").focus();
+ }
+ function typeIntoMe() {
+ setTimeout(function() {
+ sendString("س");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug646382-2-ref.html b/layout/base/tests/bug646382-2-ref.html
new file mode 100644
index 0000000000..22aa1b7d1d
--- /dev/null
+++ b/layout/base/tests/bug646382-2-ref.html
@@ -0,0 +1,18 @@
+<html class="reftest-wait">
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()"
+ style="-moz-appearance: none;
+ unicode-bidi: bidi-override;">s</textarea>
+ <script>
+ var textarea = document.querySelector("textarea");
+ function start() {
+ textarea.focus();
+ textarea.selectionStart = 1; // place caret after the letter
+ textarea.selectionEnd = 1;
+ }
+ function done() {
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug646382-2.html b/layout/base/tests/bug646382-2.html
new file mode 100644
index 0000000000..5ee365990c
--- /dev/null
+++ b/layout/base/tests/bug646382-2.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="typeIntoMe()" style="-moz-appearance: none"></textarea>
+ <script>
+ function start() {
+ document.querySelector("textarea").focus();
+ }
+ function typeIntoMe() {
+ setTimeout(function() {
+ sendString("s");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug664087-1-ref.html b/layout/base/tests/bug664087-1-ref.html
new file mode 100644
index 0000000000..e0ebc128cd
--- /dev/null
+++ b/layout/base/tests/bug664087-1-ref.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <textarea rows="3" onfocus="done()" spellcheck="false" style="-moz-appearance: none">אב
+ג</textarea>
+ <script>
+ var textarea = document.querySelector("textarea");
+ function start() {
+ textarea.focus();
+ }
+ function done() {
+ synthesizeKey("KEY_ArrowLeft");
+ synthesizeKey("KEY_ArrowLeft");
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug664087-1.html b/layout/base/tests/bug664087-1.html
new file mode 100644
index 0000000000..0541788816
--- /dev/null
+++ b/layout/base/tests/bug664087-1.html
@@ -0,0 +1,25 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <textarea rows="3" onfocus="typeIntoMe()" spellcheck="false" style="-moz-appearance: none"></textarea>
+ <script>
+ function start() {
+ document.querySelector("textarea").focus();
+ }
+ function typeIntoMe() {
+ setTimeout(function() {
+ sendString("א");
+ synthesizeKey("KEY_Enter");
+ sendString("ג");
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_End");
+ sendString("ב");
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug664087-2-ref.html b/layout/base/tests/bug664087-2-ref.html
new file mode 100644
index 0000000000..a30c3e7fe5
--- /dev/null
+++ b/layout/base/tests/bug664087-2-ref.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="done()" spellcheck="false" style="-moz-appearance: none">ab
+c</textarea>
+ <script>
+ var textarea = document.querySelector("textarea");
+ function start() {
+ textarea.focus();
+ }
+ function done() {
+ synthesizeKey("KEY_ArrowRight");
+ synthesizeKey("KEY_ArrowRight");
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug664087-2.html b/layout/base/tests/bug664087-2.html
new file mode 100644
index 0000000000..250f59c887
--- /dev/null
+++ b/layout/base/tests/bug664087-2.html
@@ -0,0 +1,25 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <textarea dir="rtl" onfocus="typeIntoMe()" spellcheck="false" style="-moz-appearance: none"></textarea>
+ <script>
+ function start() {
+ document.querySelector("textarea").focus();
+ }
+ function typeIntoMe() {
+ setTimeout(function() {
+ sendString("a");
+ synthesizeKey("KEY_Enter");
+ sendString("c");
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_End");
+ sendString("b");
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug682712-1-ref.html b/layout/base/tests/bug682712-1-ref.html
new file mode 100644
index 0000000000..642d8f97a1
--- /dev/null
+++ b/layout/base/tests/bug682712-1-ref.html
@@ -0,0 +1,24 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ </head>
+ <body onload="start()">
+ <iframe srcdoc="<body contenteditable spellcheck=false>foo bar"></iframe>
+ <script>
+ function start() {
+ var iframe = document.querySelector("iframe");
+ var win = iframe.contentWindow;
+ var doc = iframe.contentDocument;
+
+ setTimeout(function() {
+ doc.body.focus();
+
+ // Now try to set the caret without moving it
+ win.getSelection().collapse(doc.body.firstChild, 1);
+
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug682712-1.html b/layout/base/tests/bug682712-1.html
new file mode 100644
index 0000000000..1cca33bf44
--- /dev/null
+++ b/layout/base/tests/bug682712-1.html
@@ -0,0 +1,32 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <iframe srcdoc="<body contenteditable spellcheck=false>foo bar"></iframe>
+ <script>
+ function start() {
+ var iframe = document.querySelector("iframe");
+ var win = iframe.contentWindow;
+ var doc = iframe.contentDocument;
+
+ // Reframe the iframe
+ iframe.style.display = "none";
+ document.body.clientWidth;
+ iframe.style.display = "";
+ document.body.clientWidth;
+
+ setTimeout(function() {
+ doc.body.focus();
+
+ // Now try to move the caret
+ win.getSelection().collapse(doc.body.firstChild, 0);
+ synthesizeKey("KEY_ArrowRight");
+
+ document.documentElement.removeAttribute("class");
+ }, 0);
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug687297_a.html b/layout/base/tests/bug687297_a.html
new file mode 100644
index 0000000000..af0010834c
--- /dev/null
+++ b/layout/base/tests/bug687297_a.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <title>Test companion for Bug 687297</title>
+ <style type="text/css"> * { font-size:9px; } </style>
+</head>
+<body>
+ <div id="test_content">ABCDEFG 0123456</div>
+</body>
+<script type="application/javascript">
+ window.onload = function() {
+ opener.report_size_a(document.getElementById("test_content").clientHeight);
+ window.location.href = "bug687297_b.html";
+ };
+</script>
+</html>
diff --git a/layout/base/tests/bug687297_b.html b/layout/base/tests/bug687297_b.html
new file mode 100644
index 0000000000..34f682354d
--- /dev/null
+++ b/layout/base/tests/bug687297_b.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
+ <title>Test companion for Bug 687297</title>
+ <style type="text/css"> * { font-size:9px; } </style>
+</head>
+<body>
+ <div id="test_content">ABCDEFG 0123456</div>
+</body>
+<script type="application/javascript">
+ window.onload = function() {
+ opener.report_size_b(document.getElementById("test_content").clientHeight);
+ window.location.href = "bug687297_c.html";
+ };
+</script>
+</html>
diff --git a/layout/base/tests/bug687297_c.html b/layout/base/tests/bug687297_c.html
new file mode 100644
index 0000000000..ea029d24e5
--- /dev/null
+++ b/layout/base/tests/bug687297_c.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <title>Test companion for Bug 687297</title>
+ <style type="text/css"> * { font-size:9px; } </style>
+</head>
+<body>
+ <div id="test_content">ABCDEFG 0123456</div>
+</body>
+<script type="application/javascript">
+ window.onload = function() {
+ opener.report_size_c(document.getElementById("test_content").clientHeight);
+ window.close();
+ };
+</script>
+</html>
diff --git a/layout/base/tests/bug746993-1-ref.html b/layout/base/tests/bug746993-1-ref.html
new file mode 100644
index 0000000000..309a7261e8
--- /dev/null
+++ b/layout/base/tests/bug746993-1-ref.html
@@ -0,0 +1,20 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <iframe srcdoc="<body contenteditable spellcheck=false>Here's some text.<br /><br /><div></div></body>"></iframe>
+ <script>
+ function start() {
+ var iframe = document.querySelector("iframe");
+ var win = iframe.contentWindow;
+ var doc = iframe.contentDocument;
+ iframe.focus();
+ doc.body.focus();
+ win.getSelection().collapse(doc.body, 3);
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug746993-1.html b/layout/base/tests/bug746993-1.html
new file mode 100644
index 0000000000..8b17db7dbe
--- /dev/null
+++ b/layout/base/tests/bug746993-1.html
@@ -0,0 +1,22 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <iframe srcdoc="<body contenteditable spellcheck=false><br /><div></div></body>"></iframe>
+ <script>
+ function start() {
+ var iframe = document.querySelector("iframe");
+ var win = iframe.contentWindow;
+ var doc = iframe.contentDocument;
+ iframe.focus();
+ doc.body.focus();
+ win.getSelection().collapse(doc.body, 0);
+ sendString("Here's some text.");
+ synthesizeKey("KEY_Enter");
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug839103.css b/layout/base/tests/bug839103.css
new file mode 100644
index 0000000000..611907d3d7
--- /dev/null
+++ b/layout/base/tests/bug839103.css
@@ -0,0 +1 @@
+* {}
diff --git a/layout/base/tests/bug851445_helper.html b/layout/base/tests/bug851445_helper.html
new file mode 100644
index 0000000000..dc4e4002e6
--- /dev/null
+++ b/layout/base/tests/bug851445_helper.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<body style="height:1000px">
+<script>
+var docElement = document.documentElement;
+docElement.style.display = 'none';
+docElement.offsetTop;
+docElement.style.display = '';
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug923376-ref.html b/layout/base/tests/bug923376-ref.html
new file mode 100644
index 0000000000..9f2c237265
--- /dev/null
+++ b/layout/base/tests/bug923376-ref.html
@@ -0,0 +1,12 @@
+<!doctype html>
+<html class="reftest-wait"><div contenteditable>something missspelled<br>something elsed#</div>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+document.body.firstChild.focus();
+var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.import(
+ "resource://testing-common/AsyncSpellCheckTestHelper.jsm"
+);
+maybeOnSpellCheck(document.body.firstChild, function() {
+ document.documentElement.removeAttribute("class");
+});
+</script>
diff --git a/layout/base/tests/bug923376.html b/layout/base/tests/bug923376.html
new file mode 100644
index 0000000000..278b806253
--- /dev/null
+++ b/layout/base/tests/bug923376.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html class="reftest-wait"><div contenteditable></div>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+var div = document.body.firstChild;
+div.focus();
+var { maybeOnSpellCheck } = SpecialPowers.ChromeUtils.import(
+ "resource://testing-common/AsyncSpellCheckTestHelper.jsm"
+);
+maybeOnSpellCheck(div, function() {
+ div.innerHTML = 'something missspelled<br>something elsed#';
+ maybeOnSpellCheck(div, function() {
+ document.documentElement.removeAttribute("class");
+ });
+});
+</script>
diff --git a/layout/base/tests/bug956530-1-ref.html b/layout/base/tests/bug956530-1-ref.html
new file mode 100644
index 0000000000..d998b6d8b5
--- /dev/null
+++ b/layout/base/tests/bug956530-1-ref.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <style>
+ /* eliminate the blue glow when focusing the element. */
+ input {
+ background: none;
+ border: none;
+ outline: none;
+ }
+ </style>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script>
+ async function test() {
+ let input = document.querySelector("input");
+ let waitForFocus = new Promise(resolve => {
+ input.addEventListener("focus", resolve, {once: true});
+ });
+ window.focus();
+ input.focus();
+ await waitForFocus;
+ input.select();
+ await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ <body onload="SimpleTest.executeSoon(test);">
+ <input value="text text text text text">
+ </body>
+</html>
diff --git a/layout/base/tests/bug956530-1.html b/layout/base/tests/bug956530-1.html
new file mode 100644
index 0000000000..68cbc04258
--- /dev/null
+++ b/layout/base/tests/bug956530-1.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <style>
+ /* eliminate the blue glow when focusing the element. */
+ input {
+ background: none;
+ border: none;
+ outline: none;
+ }
+ </style>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ async function test() {
+ let input = document.querySelector("input");
+ input.setSelectionRange(input.value.length, input.value.length);
+ window.focus();
+ // Run input.onfocus
+ synthesizeMouseAtCenter(input, {});
+ // Blur
+ await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
+ synthesizeMouseAtCenter(document.body, {});
+ // Run input.onfocus again
+ await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
+ synthesizeMouseAtCenter(input, {});
+ // Check the result
+ await new Promise(resolve => { SimpleTest.executeSoon(resolve); });
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ <body onload="SimpleTest.executeSoon(test);">
+ <input value="text text text text text"
+ onfocus="this.select();">
+ </body>
+</html>
diff --git a/layout/base/tests/bug966992-1-ref.html b/layout/base/tests/bug966992-1-ref.html
new file mode 100644
index 0000000000..c8fff7b5ed
--- /dev/null
+++ b/layout/base/tests/bug966992-1-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Testcases for overflow-clip-box:content-box</title>
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font:16px monospace; padding:0; margin:7px;
+ }
+.block {
+ border:1px solid grey; height:50px; width:200px; padding:20px;
+ overflow:auto; overflow-clip-box:padding-box;
+}
+.rel { position:relative; }
+.mask1 { position:absolute; width:20px; background:white; top:0; bottom:0; right:0; }
+mask {
+ display:block;
+ position:absolute;
+ left: -1px;
+ bottom: -1px;
+ height: 25px;
+ width: 80%;
+ background:black;
+}
+ </style>
+</head>
+<body>
+
+<div style="position:relative;">
+<div contenteditable=true spellcheck=false tabindex=0 id=x class="rel block">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX<span style="padding-right:20px">X</span><div class=mask1></div></div>
+<mask></mask>
+</div>
+
+<script>
+var x = document.getElementById('x');
+x.focus();
+window.getSelection().collapse(x,0);
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug966992-1.html b/layout/base/tests/bug966992-1.html
new file mode 100644
index 0000000000..f630ff1c6b
--- /dev/null
+++ b/layout/base/tests/bug966992-1.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Testcases for overflow-clip-box:content-box</title>
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font:16px monospace; padding:0; margin:7px;
+ }
+.block {
+ border:1px solid grey; height:50px; width:200px; padding:20px;
+ overflow:auto; overflow-clip-box:content-box;
+}
+mask {
+ display:block;
+ position:absolute;
+ left: -1px;
+ bottom: -1px;
+ height: 25px;
+ width: 80%;
+ background:black;
+}
+ </style>
+</head>
+<body>
+
+<div style="position:relative;">
+<div contenteditable=true spellcheck=false tabindex=0 id=x class="block">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
+<mask></mask>
+</div>
+<script>
+var x = document.getElementById('x');
+x.focus();
+window.getSelection().collapse(x,0);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug966992-2-ref.html b/layout/base/tests/bug966992-2-ref.html
new file mode 100644
index 0000000000..a619d579c6
--- /dev/null
+++ b/layout/base/tests/bug966992-2-ref.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Testcases for overflow-clip-box:content-box</title>
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font:16px monospace; padding:0; margin:7px;
+ }
+.block {
+ border:1px solid grey; height:50px; width:200px; padding:20px;
+ overflow:auto; overflow-clip-box:padding-box;
+ line-height:1px;
+}
+.rel { position:relative; }
+.mask1 { position:absolute; width:20px; background:white; top:0; bottom:0; right:0; }
+.mask2 { position:absolute; height:20px; background:white; top:0; left:40px; right:0; }
+mask {
+ display:block;
+ position:absolute;
+ left: -1px;
+ bottom: -1px;
+ height: 25px;
+ width: 80%;
+ background:black;
+}
+ </style>
+</head>
+<body>
+
+<div style="position:relative;">
+<div contenteditable=true spellcheck=false tabindex=0 id=x class="rel block">&nbsp;&nbsp;&nbsp;&nbsp;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX<span style="padding-right:20px">X</span><div class=mask2></div><div class=mask1></div></div>
+<mask></mask>
+</div>
+
+<script>
+var x = document.getElementById('x');
+x.focus();
+window.getSelection().collapse(x,0);
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/bug966992-2.html b/layout/base/tests/bug966992-2.html
new file mode 100644
index 0000000000..1a8919e55d
--- /dev/null
+++ b/layout/base/tests/bug966992-2.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Testcases for overflow-clip-box:content-box</title>
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font:16px monospace; padding:0; margin:7px;
+ }
+.block {
+ border:1px solid grey; height:50px; width:200px; padding:20px;
+ overflow:auto; overflow-clip-box:content-box;
+ line-height:1px;
+}
+.rel { position:relative; }
+mask {
+ display:block;
+ position:absolute;
+ left: -1px;
+ bottom: -1px;
+ height: 25px;
+ width: 80%;
+ background:black;
+}
+ </style>
+</head>
+<body>
+
+<div style="position:relative;">
+<div contenteditable=true spellcheck=false tabindex=0 id=x class="block">&nbsp;&nbsp;&nbsp;&nbsp;XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
+<mask></mask>
+</div>
+<script>
+var x = document.getElementById('x');
+x.focus();
+window.getSelection().collapse(x,0);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug966992-3-ref.html b/layout/base/tests/bug966992-3-ref.html
new file mode 100644
index 0000000000..9974ccdf48
--- /dev/null
+++ b/layout/base/tests/bug966992-3-ref.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Reference case for overflow-clip-box:content-box</title>
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font:16px monospace; padding:0; margin:7px;
+ }
+ div {
+ width: 100px; padding-right:50px; overflow-clip-box:padding-box;
+ overflow:hidden;
+ }
+ </style>
+</head>
+<body>
+
+<div contenteditable=true spellcheck=false tabindex=0 id=x class="block"><span style="padding-right:50px">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</span></div>
+
+<script>
+var x = document.getElementById('x');
+x.focus();
+x.scrollLeft=100000
+window.getSelection().collapse(x,1);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug966992-3.html b/layout/base/tests/bug966992-3.html
new file mode 100644
index 0000000000..2cc2af96a7
--- /dev/null
+++ b/layout/base/tests/bug966992-3.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Testcases for overflow-clip-box:content-box</title>
+ <style type="text/css">
+ html,body {
+ color:black; background-color:white; font:16px monospace; padding:0; margin:7px;
+ }
+ div {
+ width: 100px; padding-right:50px; overflow-clip-box:content-box;
+ overflow:hidden;
+ }
+ </style>
+</head>
+<body>
+
+<div contenteditable=true spellcheck=false tabindex=0 id=x class="block">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</div>
+
+<script>
+var x = document.getElementById('x');
+x.focus();
+x.scrollLeft=100000
+window.getSelection().collapse(x,1);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/bug970964_inner.html b/layout/base/tests/bug970964_inner.html
new file mode 100644
index 0000000000..e8e7092aea
--- /dev/null
+++ b/layout/base/tests/bug970964_inner.html
@@ -0,0 +1,357 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=970964
+-->
+<head>
+ <title>Test for Bug 970964</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=970964">Mozilla Bug 970964</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 970964 **/
+
+function ok(condition, msg) {
+ parent.ok(condition, msg);
+}
+
+function is(a, b, msg) {
+ parent.is(a, b, msg);
+}
+
+function testtouch(aOptions) {
+ if (!aOptions)
+ aOptions = {};
+ this.identifier = aOptions.identifier || 0;
+ this.target = aOptions.target || 0;
+ this.page = aOptions.page || {x: 0, y: 0};
+ this.radius = aOptions.radius || {x: 0, y: 0};
+ this.rotationAngle = aOptions.rotationAngle || 0;
+ this.force = aOptions.force || 1;
+}
+
+function touchEvent(aOptions) {
+ if (!aOptions) {
+ aOptions = {};
+ }
+ this.ctrlKey = aOptions.ctrlKey || false;
+ this.altKey = aOptions.altKey || false;
+ this.shiftKey = aOptions.shiftKey || false;
+ this.metaKey = aOptions.metaKey || false;
+ this.touches = aOptions.touches || [];
+ this.targetTouches = aOptions.targetTouches || [];
+ this.changedTouches = aOptions.changedTouches || [];
+}
+
+function sendTouchEvent(windowUtils, aType, aEvent, aModifiers) {
+ var ids = [], xs=[], ys=[], rxs = [], rys = [],
+ rotations = [], forces = [], tiltXs = [], tiltYs = [], twists = [];
+
+ for (var touchType of ["touches", "changedTouches", "targetTouches"]) {
+ for (var i = 0; i < aEvent[touchType].length; i++) {
+ if (!ids.includes(aEvent[touchType][i].identifier)) {
+ ids.push(aEvent[touchType][i].identifier);
+ xs.push(aEvent[touchType][i].page.x);
+ ys.push(aEvent[touchType][i].page.y);
+ rxs.push(aEvent[touchType][i].radius.x);
+ rys.push(aEvent[touchType][i].radius.y);
+ rotations.push(aEvent[touchType][i].rotationAngle);
+ forces.push(aEvent[touchType][i].force);
+ tiltXs.push(0);
+ tiltYs.push(0);
+ twists.push(0);
+ }
+ }
+ }
+ return windowUtils.sendTouchEvent(aType,
+ ids, xs, ys, rxs, rys,
+ rotations, forces, tiltXs, tiltYs, twists,
+ aModifiers, 0);
+}
+
+function getDefaultArgEvent(eventname) {
+ return new PointerEvent(eventname, {
+ bubbles: true, cancelable: true, view: window,
+ detail: 0, screenX: 0, screenY: 0, clientX: 0, clientY: 0,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ button: 0, relatedTarget: null, pointerId: 0
+ });
+}
+
+function getTouchEventForTarget(target, cwu, id) {
+ var bcr = target.getBoundingClientRect();
+ var touch = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target: target,
+ identifier: id,
+ });
+ var event = new touchEvent({
+ touches: [touch],
+ targetTouches: [touch],
+ changedTouches: [touch]
+ });
+ return event;
+}
+
+function runTests() {
+ var d0 = document.getElementById("d0");
+ var d1 = document.getElementById("d1");
+ var d2 = document.getElementById("d2");
+ var d3 = document.getElementById("d3");
+
+ // Test Pointer firing before any mouse/touch original source
+
+ var mouseDownTriggered = 0;
+ var pointerDownTriggered = 0;
+ var touchDownTriggered = 0;
+ var touchCancelTriggered = 0;
+ var pointerCancelTriggered = 0;
+
+ // Test pointer event generated from mouse event
+ d0.addEventListener("mousedown", (e) => {
+ ++mouseDownTriggered;
+ is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("pointerdown", (e) => {
+ ++pointerDownTriggered;
+ is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+ }, {once: true});
+
+ synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+ synthesizeMouse(d1, 3, 3, { type: "mousedown"});
+ synthesizeMouse(d1, 3, 3, { type: "mouseup"});
+
+ // Test pointer event generated from touch event
+ mouseDownTriggered = 0;
+ pointerDownTriggered = 0;
+
+ d0.addEventListener("touchstart", (e) => {
+ ++touchDownTriggered;
+ is(pointerDownTriggered, touchDownTriggered, "Touch event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("mousedown", (e) => {
+ ++mouseDownTriggered;
+ is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("pointerdown", (e) => {
+ ++pointerDownTriggered;
+ is(pointerDownTriggered, touchDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+ is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+ }, {once: true});
+
+ d0.addEventListener("touchcancel", (e) => {
+ ++touchCancelTriggered;
+ is(pointerCancelTriggered, touchCancelTriggered, "Touch cancel event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("pointercancel", function(ev) {
+ is(ev.pointerId, 0, "Correct default pointerId");
+ is(ev.bubbles, true, "bubbles should be true");
+ is(ev.cancelable, false, "pointercancel cancelable should be false ");
+ ++pointerCancelTriggered;
+ is(pointerCancelTriggered, touchCancelTriggered + 1, "Pointer event must be triggered before touch event!");
+ }, {once: true});
+
+ var cwu = SpecialPowers.getDOMWindowUtils(window);
+ var event1 = getTouchEventForTarget(d1, cwu, 0);
+ sendTouchEvent(cwu, "touchstart", event1, 0);
+ sendTouchEvent(cwu, "touchmove", event1, 0);
+ // Test Touch to Pointer Cancel
+ sendTouchEvent(cwu, "touchcancel", event1, 0);
+
+ // Check Pointer enter/leave from mouse generated event
+ var mouseEnterTriggered = 0;
+ var pointerEnterTriggered = 0;
+ d2.onpointerenter = function(e) {
+ pointerEnterTriggered = 1;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ is(mouseEnterTriggered, 0, "Pointer event must be triggered before mouse event!");
+ };
+ d2.onmouseenter = function(e) {
+ mouseEnterTriggered = 1;
+ is(pointerEnterTriggered , 1, "Mouse event must be triggered after pointer event!");
+ };
+ synthesizeMouse(d2, 3, 3, { type: "mousemove"});
+ d2.onmouseenter = function(e) {}
+
+ // Test Multi Pointer enter/leave for pointers generated from Mouse and Touch at the same time
+ // Enter mouse and touch generated pointers to different elements
+ var d1enterCount = 0;
+ var d2enterCount = 0;
+ var d3enterCount = 0;
+ var d1leaveCount = 0;
+ var d2leaveCount = 0;
+ var d3leaveCount = 0;
+ var mousePointerEnterLeaveCount = 0;
+ var touchPointerEnterLeaveCount = 0;
+
+ var checkPointerType = function(pointerType) {
+ if (pointerType == "mouse") {
+ ++mousePointerEnterLeaveCount;
+ } else if (pointerType == "touch") {
+ ++touchPointerEnterLeaveCount;
+ }
+ };
+
+ d1.onpointerenter = function(e) {
+ ++d1enterCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d2.onpointerenter = function(e) {
+ ++d2enterCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d3.onpointerenter = function(e) {
+ ++d3enterCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d1.onpointerleave = function(e) {
+ ++d1leaveCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d2.onpointerleave = function(e) {
+ ++d2leaveCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d3.onpointerleave = function(e) {
+ ++d3leaveCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+
+ synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+ sendTouchEvent(cwu, "touchstart", getTouchEventForTarget(d3, cwu, 3), 0);
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d3, cwu, 3), 0);
+ is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 2, "Wrong mouse enterLeave count for!");
+
+ is(d1enterCount, 1, "Wrong enter count for! d1");
+ is(d2leaveCount, 1, "Wrong leave count for! d2");
+ is(d3enterCount, 1, "Wrong enter count for! d3");
+
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 3), 0);
+ synthesizeMouse(d3, 3, 3, { type: "mousemove"});
+ is(touchPointerEnterLeaveCount, 3, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 4, "Wrong mouse enterLeave count for!");
+
+ is(d3leaveCount, 1, "Wrong leave count for! d3");
+ is(d1leaveCount, 1, "Wrong leave count for! d1");
+ is(d1enterCount, 2, "Wrong enter count for! d1");
+ is(d3enterCount, 2, "Wrong enter count for! d3");
+
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d2, cwu, 3), 0);
+ synthesizeMouse(d2, 3, 3, { type: "mousemove"});
+ is(touchPointerEnterLeaveCount, 5, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 6, "Wrong mouse enterLeave count for!");
+
+ is(d1leaveCount, 2, "Wrong leave count for! d1");
+ is(d2enterCount, 2, "Wrong enter count for! d2");
+ is(d3leaveCount, 2, "Wrong leave count for! d3");
+
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 3), 0);
+ synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+ is(touchPointerEnterLeaveCount, 7, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 8, "Wrong mouse enterLeave count for!");
+
+ is(d2leaveCount, 3, "Wrong leave count for! d2");
+ is(d1enterCount, 4, "Wrong enter count for! d1");
+
+ // Test for pointer buttons when it generated from mousemove event
+ d1.onpointermove = function(e) {
+ is(e.buttons, 0, "Buttons must be 0 on pointer generated from mousemove");
+ is(e.button, -1, "Button must be -1 on pointer generated from mousemove when no buttons pressed");
+ is(e.pointerType, "mouse", "Pointer type must be mouse");
+ };
+ cwu.sendMouseEvent("mousemove", 4, 4, 0, 0, 0, false, 0, 0);
+
+ d1.onpointermove = function(e) {
+ is(e.buttons, 1, "Buttons must be 1 on pointermove generated from touch event");
+ is(e.button, -1, "Button must be -1 on pointermove generated from touch event");
+ is(e.pointerType, "touch", "Pointer type must be touch");
+ };
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 2), 0);
+
+ // Test for cancel trigger pointerOut (Touch Pointer must be at d1 now)
+ pointerCancelTriggered = 0;
+ var pointerOutTriggeredForCancelEvent = 0;
+ var pointerLeaveTriggeredForCancelEvent = 0;
+ d1.onpointerout = function(e) {
+ if (pointerOutTriggeredForCancelEvent == 0) {
+ is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");
+ is(e.pointerType, "touch", "Wrong Pointer type, should be touch type");
+ } else {
+ is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event");
+ is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
+ }
+ pointerOutTriggeredForCancelEvent = 1;
+ };
+ d1.onpointerleave = function(e) {
+ is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out must be dispatched bedore Pointer leave");
+ if (pointerLeaveTriggeredForCancelEvent == 0) {
+ is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");
+ is(e.pointerType, "touch", "Wrong Pointer type, should be touch type");
+ } else {
+ is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event");
+ is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
+ }
+ pointerLeaveTriggeredForCancelEvent = 1;
+ }
+
+ sendTouchEvent(cwu, "touchcancel", getTouchEventForTarget(d1, cwu, 3), 0);
+ is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out not dispatched on PointerCancel");
+ is(pointerLeaveTriggeredForCancelEvent, 1, "Pointer Leave not dispatched on PointerCancel");
+
+ finishTest();
+}
+
+function finishTest() {
+ // Let window.onerror have a chance to fire
+ setTimeout(function() {
+ setTimeout(function() {
+ window.parent.postMessage("run next", "*");
+ }, 0);
+ }, 0);
+}
+
+window.onload = function () {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.w3c_pointer_events.implicit_capture", false]
+ ]
+ }, runTests);
+}
+
+</script>
+</pre>
+<div id="d0">
+Test divs --
+<div id="d1">t</div><div id="d2">t</div><div id="d3">t</div>
+--
+</div>
+</body>
+</html>
diff --git a/layout/base/tests/bug970964_inner2.html b/layout/base/tests/bug970964_inner2.html
new file mode 100644
index 0000000000..51cee6c1ee
--- /dev/null
+++ b/layout/base/tests/bug970964_inner2.html
@@ -0,0 +1,356 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=970964
+-->
+<head>
+ <title>Test for Bug 970964</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=970964">Mozilla Bug 970964</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 970964 **/
+
+function ok(condition, msg) {
+ parent.ok(condition, msg);
+}
+
+function is(a, b, msg) {
+ parent.is(a, b, msg);
+}
+
+function testtouch(aOptions) {
+ if (!aOptions)
+ aOptions = {};
+ this.identifier = aOptions.identifier || 0;
+ this.target = aOptions.target || 0;
+ this.page = aOptions.page || {x: 0, y: 0};
+ this.radius = aOptions.radius || {x: 0, y: 0};
+ this.rotationAngle = aOptions.rotationAngle || 0;
+ this.force = aOptions.force || 1;
+}
+
+function touchEvent(aOptions) {
+ if (!aOptions) {
+ aOptions = {};
+ }
+ this.ctrlKey = aOptions.ctrlKey || false;
+ this.altKey = aOptions.altKey || false;
+ this.shiftKey = aOptions.shiftKey || false;
+ this.metaKey = aOptions.metaKey || false;
+ this.touches = aOptions.touches || [];
+ this.targetTouches = aOptions.targetTouches || [];
+ this.changedTouches = aOptions.changedTouches || [];
+}
+
+function sendTouchEvent(windowUtils, aType, aEvent, aModifiers) {
+ var ids = [], xs=[], ys=[], rxs = [], rys = [],
+ rotations = [], forces = [], tiltXs = [], tiltYs = [], twists = [];
+
+ for (var touchType of ["touches", "changedTouches", "targetTouches"]) {
+ for (var i = 0; i < aEvent[touchType].length; i++) {
+ if (!ids.includes(aEvent[touchType][i].identifier)) {
+ ids.push(aEvent[touchType][i].identifier);
+ xs.push(aEvent[touchType][i].page.x);
+ ys.push(aEvent[touchType][i].page.y);
+ rxs.push(aEvent[touchType][i].radius.x);
+ rys.push(aEvent[touchType][i].radius.y);
+ rotations.push(aEvent[touchType][i].rotationAngle);
+ forces.push(aEvent[touchType][i].force);
+ tiltXs.push(0);
+ tiltYs.push(0);
+ twists.push(0);
+ }
+ }
+ }
+ return windowUtils.sendTouchEvent(aType,
+ ids, xs, ys, rxs, rys,
+ rotations, forces, tiltXs, tiltYs, twists,
+ aModifiers, 0);
+}
+
+function getDefaultArgEvent(eventname) {
+ return new PointerEvent(eventname, {
+ bubbles: true, cancelable: true, view: window,
+ detail: 0, screenX: 0, screenY: 0, clientX: 0, clientY: 0,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ button: 0, relatedTarget: null, pointerId: 0
+ });
+}
+
+function getTouchEventForTarget(target, cwu, id) {
+ var bcr = target.getBoundingClientRect();
+ var touch = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target: target,
+ identifier: id,
+ });
+ var event = new touchEvent({
+ touches: [touch],
+ targetTouches: [touch],
+ changedTouches: [touch]
+ });
+ return event;
+}
+
+function runTests() {
+ var d0 = document.getElementById("d0");
+ var d1 = document.getElementById("d1");
+ var d2 = document.getElementById("d2");
+ var d3 = document.getElementById("d3");
+
+ // Test Pointer firing before any mouse/touch original source
+
+ var mouseDownTriggered = 0;
+ var pointerDownTriggered = 0;
+ var touchDownTriggered = 0;
+ var touchCancelTriggered = 0;
+ var pointerCancelTriggered = 0;
+
+ // Test pointer event generated from mouse event
+ d0.addEventListener("mousedown", (e) => {
+ ++mouseDownTriggered;
+ is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("pointerdown", (e) => {
+ ++pointerDownTriggered;
+ is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+ }, {once: true});
+
+ synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+ synthesizeMouse(d1, 3, 3, { type: "mousedown"});
+ synthesizeMouse(d1, 3, 3, { type: "mouseup"});
+
+ // Test pointer event generated from touch event
+ mouseDownTriggered = 0;
+ pointerDownTriggered = 0;
+
+ d0.addEventListener("touchstart", (e) => {
+ ++touchDownTriggered;
+ is(pointerDownTriggered, touchDownTriggered, "Touch event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("mousedown", (e) => {
+ ++mouseDownTriggered;
+ is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("pointerdown", (e) => {
+ ++pointerDownTriggered;
+ is(pointerDownTriggered, touchDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+ is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+ }, {once: true});
+
+ d0.addEventListener("touchcancel", (e) => {
+ ++touchCancelTriggered;
+ is(pointerCancelTriggered, touchCancelTriggered, "Touch cancel event must be triggered after pointer event!");
+ }, {once: true});
+
+ d0.addEventListener("pointercancel", function(ev) {
+ is(ev.pointerId, 0, "Correct default pointerId");
+ is(ev.bubbles, true, "bubbles should be true");
+ is(ev.cancelable, false, "pointercancel cancelable should be false ");
+ ++pointerCancelTriggered;
+ is(pointerCancelTriggered, touchCancelTriggered + 1, "Pointer event must be triggered before touch event!");
+ }, {once: true});
+
+ var cwu = SpecialPowers.getDOMWindowUtils(window);
+ var event1 = getTouchEventForTarget(d1, cwu, 0);
+ sendTouchEvent(cwu, "touchstart", event1, 0);
+ sendTouchEvent(cwu, "touchmove", event1, 0);
+ // Test Touch to Pointer Cancel
+ sendTouchEvent(cwu, "touchcancel", event1, 0);
+
+ // Check Pointer enter/leave from mouse generated event
+ var mouseEnterTriggered = 0;
+ var pointerEnterTriggered = 0;
+ d2.onpointerenter = function(e) {
+ pointerEnterTriggered = 1;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ is(mouseEnterTriggered, 0, "Pointer event must be triggered before mouse event!");
+ };
+ d2.onmouseenter = function(e) {
+ mouseEnterTriggered = 1;
+ is(pointerEnterTriggered , 1, "Mouse event must be triggered after pointer event!");
+ };
+ synthesizeMouse(d2, 3, 3, { type: "mousemove"});
+ d2.onmouseenter = function(e) {}
+
+ // Test Multi Pointer enter/leave for pointers generated from Mouse and Touch at the same time
+ // Enter mouse and touch generated pointers to different elements
+ var d1enterCount = 0;
+ var d2enterCount = 0;
+ var d3enterCount = 0;
+ var d1leaveCount = 0;
+ var d2leaveCount = 0;
+ var d3leaveCount = 0;
+ var mousePointerEnterLeaveCount = 0;
+ var touchPointerEnterLeaveCount = 0;
+
+ var checkPointerType = function(pointerType) {
+ if (pointerType == "mouse") {
+ ++mousePointerEnterLeaveCount;
+ } else if (pointerType == "touch") {
+ ++touchPointerEnterLeaveCount;
+ }
+ };
+
+ d1.onpointerenter = function(e) {
+ ++d1enterCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d2.onpointerenter = function(e) {
+ ++d2enterCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d3.onpointerenter = function(e) {
+ ++d3enterCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d1.onpointerleave = function(e) {
+ ++d1leaveCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d2.onpointerleave = function(e) {
+ ++d2leaveCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+ d3.onpointerleave = function(e) {
+ ++d3leaveCount;
+ is(e.bubbles, false, "bubbles should be false");
+ is(e.cancelable, false, "cancelable should be false");
+ checkPointerType(e.pointerType);
+ };
+
+ synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+ sendTouchEvent(cwu, "touchstart", getTouchEventForTarget(d3, cwu, 3), 0);
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d3, cwu, 3), 0);
+ is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 2, "Wrong mouse enterLeave count for!");
+
+ is(d1enterCount, 1, "Wrong enter count for! d1");
+ is(d2leaveCount, 1, "Wrong leave count for! d2");
+ is(d3enterCount, 1, "Wrong enter count for! d3");
+
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 3), 0);
+ synthesizeMouse(d3, 3, 3, { type: "mousemove"});
+ is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 4, "Wrong mouse enterLeave count for!");
+
+ is(d3leaveCount, 0, "Wrong leave count for! d3");
+ is(d1leaveCount, 1, "Wrong leave count for! d1");
+ is(d1enterCount, 1, "Wrong enter count for! d1");
+ is(d3enterCount, 2, "Wrong enter count for! d3");
+
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d2, cwu, 3), 0);
+ synthesizeMouse(d2, 3, 3, { type: "mousemove"});
+ is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 6, "Wrong mouse enterLeave count for!");
+
+ is(d1leaveCount, 1, "Wrong leave count for! d1");
+ is(d2enterCount, 1, "Wrong enter count for! d2");
+ is(d3leaveCount, 1, "Wrong leave count for! d3");
+
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 3), 0);
+ synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+ is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
+ is(mousePointerEnterLeaveCount, 8, "Wrong mouse enterLeave count for!");
+
+ is(d2leaveCount, 2, "Wrong leave count for! d2");
+ is(d1enterCount, 2, "Wrong enter count for! d1");
+
+ // Test for pointer buttons when it generated from mousemove event
+ d1.onpointermove = function(e) {
+ is(e.buttons, 0, "Buttons must be 0 on pointer generated from mousemove");
+ is(e.button, -1, "Button must be -1 on pointer generated from mousemove when no buttons pressed");
+ is(e.pointerType, "mouse", "Pointer type must be mouse");
+ };
+ cwu.sendMouseEvent("mousemove", 4, 4, 0, 0, 0, false, 0, 0);
+
+ d1.onpointermove = function(e) {
+ is(e.buttons, 1, "Buttons must be 1 on pointermove generated from touch event");
+ is(e.button, -1, "Button must be -1 on pointermove generated from touch event");
+ is(e.pointerType, "touch", "Pointer type must be touch");
+ };
+ sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d1, cwu, 2), 0);
+
+ // Test for cancel trigger pointerOut (Touch Pointer must be at d1 now)
+ pointerCancelTriggered = 0;
+ var pointerOutTriggeredForCancelEvent = 0;
+ var pointerLeaveTriggeredForCancelEvent = 0;
+ d3.onpointerout = function(e) {
+ if (pointerOutTriggeredForCancelEvent == 0) {
+ is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");
+ is(e.pointerType, "touch", "Wrong Pointer type, should be touch type");
+ } else {
+ is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event");
+ is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
+ }
+ pointerOutTriggeredForCancelEvent = 1;
+ };
+ d3.onpointerleave = function(e) {
+ is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out must be dispatched bedore Pointer leave");
+ if (pointerLeaveTriggeredForCancelEvent == 0) {
+ is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");
+ is(e.pointerType, "touch", "Wrong Pointer type, should be touch type");
+ } else {
+ is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event");
+ is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
+ }
+ pointerLeaveTriggeredForCancelEvent = 1;
+ }
+ sendTouchEvent(cwu, "touchcancel", getTouchEventForTarget(d1, cwu, 3), 0);
+ is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out not dispatched on PointerCancel");
+ is(pointerLeaveTriggeredForCancelEvent, 1, "Pointer Leave not dispatched on PointerCancel");
+
+ finishTest();
+}
+
+function finishTest() {
+ // Let window.onerror have a chance to fire
+ setTimeout(function() {
+ setTimeout(function() {
+ window.parent.postMessage("finishTest", "*");
+ }, 0);
+ }, 0);
+}
+
+window.onload = function () {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.w3c_pointer_events.implicit_capture", true]
+ ]
+ }, runTests);
+}
+
+</script>
+</pre>
+<div id="d0">
+Test divs --
+<div id="d1">t</div><div id="d2">t</div><div id="d3">t</div>
+--
+</div>
+</body>
+</html>
diff --git a/layout/base/tests/bug977003_inner_1.html b/layout/base/tests/bug977003_inner_1.html
new file mode 100644
index 0000000000..774bfb43e2
--- /dev/null
+++ b/layout/base/tests/bug977003_inner_1.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+https://bugzilla.mozilla.org/show_bug.cgi?id=1094913
+https://bugzilla.mozilla.org/show_bug.cgi?id=1098139
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bugs 977003, 1094913, 1098139</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target{ background: yellow; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var test_send_got = 0;
+ var test_got_async = 0;
+ var test_got_type = "";
+ var test_got_primary = false;
+ var test_send_lost = 0;
+ var test_lost_async = 0;
+ var test_lost_type = "";
+ var test_lost_primary = false;
+
+ function DownHandler(event) {
+ logger("Receive event: " + event.type);
+ logger("Send setPointerCapture to target");
+ target.setPointerCapture(event.pointerId);
+ logger("setPointerCapture was executed");
+ test_send_got++;
+ }
+ function GotPCHandler(event) {
+ logger("Receive event: " + event.type + "(" + event.pointerType + ")");
+ if(test_send_got)
+ test_got_async++;
+ test_got_type = event.pointerType;
+ test_got_primary = event.isPrimary;
+ logger("Send releasePointerCapture from target");
+ target.releasePointerCapture(event.pointerId);
+ logger("releasePointerCapture was executed");
+ test_send_lost++;
+ }
+ function LostPCHandler(event) {
+ logger("Received event: " + event.type + "(" + event.pointerType + ")");
+ if(test_send_lost)
+ test_lost_async++;
+ test_lost_type = event.pointerType;
+ test_lost_primary = event.isPrimary;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ target.addEventListener("pointerdown", DownHandler);
+ target.addEventListener("gotpointercapture", GotPCHandler);
+ target.addEventListener("lostpointercapture", LostPCHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mouseup"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_send_got, 1, "Part 1: gotpointercapture event should be sent once");
+ parent.is(test_got_async, 1, "Part 1: gotpointercapture event should be asynchronous");
+ parent.is(test_got_type, "mouse", "Part 1: gotpointercapture event should have pointerType mouse");
+ parent.is(test_got_primary, true, "Part 1: gotpointercapture event should have isPrimary as true");
+ parent.is(test_send_lost, 1, "Part 1: lostpointercapture event should be sent once");
+ parent.is(test_lost_async, 1, "Part 1: lostpointercapture event should be asynchronous");
+ parent.is(test_lost_type, "mouse", "Part 1: lostpointercapture event should have pointerType mouse");
+ parent.is(test_lost_primary, true, "Part 1: lostpointercapture event should have isPrimary as true");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=977003">Mozilla Bug 977003 Test 1</a>
+ <br><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094913">Mozilla Bug 1094913</a>
+ <br><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1098139">Mozilla Bug 1098139</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="target">div id=target</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug977003_inner_2.html b/layout/base/tests/bug977003_inner_2.html
new file mode 100644
index 0000000000..cf7e6d729d
--- /dev/null
+++ b/layout/base/tests/bug977003_inner_2.html
@@ -0,0 +1,75 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 977003</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #listener { background: yellow; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var listener = undefined;
+ var test_down_got = false;
+ var test_listener = false;
+
+ function TargetDownHandler(event) {
+ logger("Target receive event: " + event.type);
+ logger("Send setPointerCapture to listener");
+ listener.setPointerCapture(event.pointerId);
+ logger("Send releasePointerCapture from listener");
+ listener.releasePointerCapture(event.pointerId);
+ logger("set/release was executed");
+ test_down_got = true;
+ }
+ function ListenerHandler(event) {
+ logger("Receive event on Listener: " + event.type);
+ test_listener = true;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetDownHandler);
+ listener.addEventListener("gotpointercapture", ListenerHandler);
+ listener.addEventListener("lostpointercapture", ListenerHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mouseup"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_down_got, true, "Part 2: pointerdown event should be received by target");
+ parent.is(test_listener, false, "Part 2: listener should not receive any events");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=977003">Mozilla Bug 977003 Test 2</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="listener">div id=listener</div>
+ <div id="target">div id=target</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug977003_inner_3.html b/layout/base/tests/bug977003_inner_3.html
new file mode 100644
index 0000000000..2448b4ada8
--- /dev/null
+++ b/layout/base/tests/bug977003_inner_3.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 977003</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #mediator, #listener { background: yellow; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var mediator = undefined;
+ var listener = undefined;
+ var test_down_got = false;
+ var test_mediator_got = false;
+ var test_mediator_lost = false;
+ var test_listener = false;
+
+ function TargetDownHandler(event) {
+ logger("Target receive event: " + event.type);
+ logger("Send pointerCapture to Mediator");
+ mediator.setPointerCapture(event.pointerId);
+ logger("setPointerCapture was executed");
+ test_down_got = true;
+ }
+ function MediatorGotPCHandler(event) {
+ logger("Mediator receive event: " + event.type);
+ logger("Try send setPointerCapture on listener");
+ listener.setPointerCapture(event.pointerId);
+ logger("Try send releasePointerCapture from listener");
+ listener.releasePointerCapture(event.pointerId);
+ test_mediator_got = true;
+ }
+ function MediatorLostPCHandler(event) {
+ logger("Mediator receive event: " + event.type);
+ test_mediator_lost = true;
+ }
+ function ListenerHandler(event) {
+ logger("Receive event on Listener: " + event.type);
+ test_listener = true;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ mediator = document.getElementById("mediator");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetDownHandler);
+ mediator.addEventListener("gotpointercapture", MediatorGotPCHandler);
+ mediator.addEventListener("lostpointercapture", MediatorLostPCHandler);
+ listener.addEventListener("gotpointercapture", ListenerHandler);
+ listener.addEventListener("lostpointercapture", ListenerHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/3, rect.height/3, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/4, rect.height/4, {type: "mousemove"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_down_got, true, "Part 3: pointerdown event should be received");
+ parent.is(test_mediator_got, true, "Part 3: gotpointercapture event should be received by Mediator");
+ parent.is(test_mediator_lost, true, "Part 3: lostpointercapture event should be received by Mediator");
+ parent.is(test_listener, false, "Part 3: listener should not receive any events");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=977003">Mozilla Bug 977003 Test 3</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="listener">div id=listener</div>
+ <div id="mediator">div id=mediator</div>
+ <div id="target">div id=target</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug977003_inner_4.html b/layout/base/tests/bug977003_inner_4.html
new file mode 100644
index 0000000000..2ea4a83568
--- /dev/null
+++ b/layout/base/tests/bug977003_inner_4.html
@@ -0,0 +1,100 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 977003</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #mediator, #listener { background: yellow; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var mediator = undefined;
+ var listener = undefined;
+ var test_down_got = false;
+ var test_mediator_got = false;
+ var test_mediator_lost = false;
+ var test_listener_got = false;
+ var test_listener_lost = false;
+
+ function TargetDownHandler(event) {
+ logger("Target receive event: " + event.type);
+ logger("Send pointerCapture to Mediator");
+ mediator.setPointerCapture(event.pointerId);
+ logger("setPointerCapture was executed");
+ test_down_got = true;
+ }
+ function MediatorGotPCHandler(event) {
+ logger("Mediator receive event: " + event.type);
+ logger("Try send setPointerCapture on listener");
+ listener.setPointerCapture(event.pointerId);
+ logger("Try send releasePointerCapture from Mediator");
+ mediator.releasePointerCapture(event.pointerId);
+ test_mediator_got = true;
+ }
+ function MediatorLostPCHandler(event) {
+ logger("Mediator receive event: " + event.type);
+ test_mediator_lost = true;
+ }
+ function ListenerGotHandler(event) {
+ test_listener_got = true;
+ }
+ function ListenerLostHandler(event) {
+ test_listener_lost = true;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ mediator = document.getElementById("mediator");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetDownHandler);
+ mediator.addEventListener("gotpointercapture", MediatorGotPCHandler);
+ mediator.addEventListener("lostpointercapture", MediatorLostPCHandler);
+ listener.addEventListener("gotpointercapture", ListenerGotHandler);
+ listener.addEventListener("lostpointercapture", ListenerLostHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/3, rect.height/3, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/4, rect.height/4, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/4, rect.height/4, {type: "mouseup"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_down_got, true, "Part 4: pointerdown event should be received");
+ parent.is(test_mediator_got, true, "Part 4: gotpointercapture event should be received by Mediator");
+ parent.is(test_mediator_lost, true, "Part 4: lostpointercapture event should be received by Mediator");
+ parent.is(test_listener_got, true, "Part 4: gotpointercapture event should be received by listener");
+ parent.is(test_listener_lost, true, "Part 4: lostpointercapture event should be received by listener");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=977003">Mozilla Bug 977003 Test 4</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="listener">div id=listener</div>
+ <div id="mediator">div id=mediator</div>
+ <div id="target">div id=target</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug977003_inner_5.html b/layout/base/tests/bug977003_inner_5.html
new file mode 100644
index 0000000000..73ca41acd3
--- /dev/null
+++ b/layout/base/tests/bug977003_inner_5.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+https://bugzilla.mozilla.org/show_bug.cgi?id=1073563
+https://bugzilla.mozilla.org/show_bug.cgi?id=1094913
+https://bugzilla.mozilla.org/show_bug.cgi?id=1098139
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bugs 977003, 1073563, 1094913, 1098139</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #listener { background: yellow; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var listener = undefined;
+ var test_down_target = false;
+ var test_got_listener = false;
+ var test_got_type = "";
+ var test_got_primary = false;
+ var test_lost_listener = false;
+ var test_lost_type = "";
+ var test_move_listener = false;
+ var test_over_listener = false;
+ var test_listener = false;
+ var test_lost_primary = false;
+
+ function TargetDownHandler(event) {
+ logger("Target receive event: " + event.type);
+ logger("Send setPointerCapture to listener");
+ listener.setPointerCapture(event.pointerId);
+ logger("setPointerCapture was executed");
+ test_down_target = true;
+ }
+ function ListenerGotPCHandler(event) {
+ logger("Receive event on Listener: " + event.type + "(" + event.pointerType + ")");
+ listener.releasePointerCapture(event.pointerId);
+ test_got_listener = true;
+ test_got_type = event.pointerType;
+ test_got_primary = event.isPrimary;
+ }
+ function ListenerLostPCHandler(event) {
+ logger("Receive event on Listener: " + event.type + "(" + event.pointerType + ")");
+ test_lost_listener = true;
+ test_lost_type = event.pointerType;
+ test_lost_primary = event.isPrimary;
+ }
+ function ListenerMoveHandler(event) {
+ logger("Receive event on Listener: " + event.type);
+ test_move_listener = true;
+ }
+ function ListenerOverHandler(event) {
+ logger("Receive event on Listener: " + event.type);
+ test_over_listener = true;
+ }
+ function ListenerHandler(event) {
+ logger("Receive event on Listener: " + event.type);
+ test_listener = true;
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetDownHandler);
+ listener.addEventListener("gotpointercapture", ListenerGotPCHandler);
+ listener.addEventListener("lostpointercapture", ListenerLostPCHandler);
+ listener.addEventListener("pointerover", ListenerOverHandler);
+ listener.addEventListener("pointermove", ListenerMoveHandler);
+ listener.addEventListener("pointerup", ListenerHandler);
+ listener.addEventListener("pointerout", ListenerHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown", isPrimary: true, inputSource: MouseEvent.MOZ_SOURCE_TOUCH});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousemove", isPrimary: true, inputSource: MouseEvent.MOZ_SOURCE_TOUCH});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mouseup", isPrimary: true, inputSource: MouseEvent.MOZ_SOURCE_TOUCH});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_down_target, true, "Part 5: pointerdown event should be received by target");
+ parent.is(test_got_listener, true, "Part 5: listener should receive gotpointercapture event");
+ parent.is(test_got_type, "touch", "Part 5: gotpointercapture event should have pointerType touch");
+ parent.is(test_got_primary, true, "Part 5: gotpointercapture event should have isPrimary as true");
+ parent.is(test_lost_listener, true, "Part 5: listener should receive lostpointercapture event");
+ parent.is(test_lost_type, "touch", "Part 5: lostpointercapture event should have pointerType touch");
+ parent.is(test_lost_primary, true, "Part 5: lostpointercapture event should have isPrimary as true");
+ parent.is(test_move_listener, true, "Part 5: gotpointercapture should be triggered by pointermove");
+ parent.is(test_over_listener, true, "Part 5: listener should receive pointerover when capturing pointer");
+ parent.is(test_listener, false, "Part 5: listener should not receive any other events");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=977003">Mozilla Bug 977003 Test 5</a>
+ <br><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1073563">Mozilla Bug 1073563</a>
+ <br><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1094913">Mozilla Bug 1094913</a>
+ <br><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1098139">Mozilla Bug 1098139</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="listener">div id=listener</div>
+ <div id="target">div id=target</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug977003_inner_6.html b/layout/base/tests/bug977003_inner_6.html
new file mode 100644
index 0000000000..b4a05122a5
--- /dev/null
+++ b/layout/base/tests/bug977003_inner_6.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+https://bugzilla.mozilla.org/show_bug.cgi?id=1073563
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bugs 977003, 1073563</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #target, #listener { background: yellow; }
+ </style>
+ <script type="application/javascript">
+ var target = undefined;
+ var listener = undefined;
+ var test_target = false;
+ var test_move = false;
+ var test_over = false;
+ var test_listener = false;
+ var receive_lostpointercapture = false;
+
+ function TargetDownHandler(event) {
+ logger("Target receive event: " + event.type);
+ logger("Send setPointerCapture to listener");
+ listener.setPointerCapture(event.pointerId);
+ logger("set/release was executed");
+ test_target = true;
+ }
+ function ListenerHandler(event) {
+ logger("Receive event on Listener: " + event.type);
+ if("gotpointercapture" == event.type) {
+ logger("Send releasePointerCapture from listener");
+ listener.releasePointerCapture(event.pointerId);
+ } else if(event.type == "lostpointercapture") {
+ // Set/release pointer capture in the event listeners of got/lostpointercapture won't take effect immediately
+ parent.is(receive_lostpointercapture, false, "Part 6: listener should receive only one lostpointercapture");
+ if (!receive_lostpointercapture) {
+ receive_lostpointercapture = true;
+ logger("Send setPointerCapture to listener");
+ listener.setPointerCapture(event.pointerId);
+ }
+ } else if(event.type == "pointermove") {
+ test_move = true;
+ } else if(event.type == "pointerover") {
+ test_over = true;
+ } else {
+ test_listener = true;
+ }
+ }
+ function logger(message) {
+ console.log(message);
+ var log = document.getElementById('log');
+ log.innerHTML = message + "<br>" + log.innerHTML;
+ }
+
+ function prepareTest() {
+ SimpleTest.executeSoon(executeTest);
+ }
+ function executeTest()
+ {
+ logger("executeTest");
+ target = document.getElementById("target");
+ listener = document.getElementById("listener");
+ target.addEventListener("pointerdown", TargetDownHandler);
+ listener.addEventListener("gotpointercapture", ListenerHandler);
+ listener.addEventListener("pointerdown", ListenerHandler);
+ listener.addEventListener("pointerover", ListenerHandler);
+ listener.addEventListener("pointermove", ListenerHandler);
+ listener.addEventListener("pointerup", ListenerHandler);
+ listener.addEventListener("pointerout", ListenerHandler);
+ listener.addEventListener("lostpointercapture", ListenerHandler);
+ var rect = target.getBoundingClientRect();
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousedown"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mousemove"});
+ synthesizeMouse(target, rect.width/2, rect.height/2, {type: "mouseup"});
+ finishTest();
+ }
+ function finishTest() {
+ parent.is(test_target, true, "Part 6: pointerdown event should be received by target");
+ // PE level 2 defines that the pending pointer capture is processed when firing next pointer events.
+ // In this test case, pointer capture release is processed when firing pointermove
+ parent.is(test_move, true, "Part 6: gotpointercapture should be triggered by pointermove");
+ parent.is(test_over, true, "Part 6: pointerover should be received when capturing pointer");
+ parent.is(test_listener, false, "Part 6: no other pointerevents should be fired before gotpointercapture except pointerover");
+ logger("finishTest");
+ parent.finishTest();
+ }
+ </script>
+</head>
+<body onload="prepareTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=977003">Mozilla Bug 977003 Test 6</a>
+ <br><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1073563">Mozilla Bug 1073563</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <div id="listener">div id=listener</div>
+ <div id="target">div id=target</div>
+ <pre id="log">
+ </pre>
+</body>
+</html>
diff --git a/layout/base/tests/bug989012-1-ref.html b/layout/base/tests/bug989012-1-ref.html
new file mode 100644
index 0000000000..6802038089
--- /dev/null
+++ b/layout/base/tests/bug989012-1-ref.html
@@ -0,0 +1,21 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <div onfocus="done()" contenteditable>foo<img alt="IMAGE">bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ // Set the caret right before "bar"
+ sel.collapse(div.lastChild, 0);
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug989012-1.html b/layout/base/tests/bug989012-1.html
new file mode 100644
index 0000000000..3d6b32854a
--- /dev/null
+++ b/layout/base/tests/bug989012-1.html
@@ -0,0 +1,24 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ </head>
+ <body onload="start()">
+ <div onfocus="setTimeout(done, 0)" contenteditable>foo<img alt="IMAGE">bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ sel.collapse(div, 0);
+ // Press Right four times to set the caret right before "bar"
+ for (var i = 0; i < 4; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug989012-2-ref.html b/layout/base/tests/bug989012-2-ref.html
new file mode 100644
index 0000000000..4b3072e7cd
--- /dev/null
+++ b/layout/base/tests/bug989012-2-ref.html
@@ -0,0 +1,26 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <style>
+ span:before {
+ content: "IMAGE";
+ }
+ </style>
+ </head>
+ <body onload="start()">
+ <div onfocus="done()" contenteditable>foo<span></span>bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ // Set the caret right before "bar"
+ sel.collapse(div.lastChild, 0);
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug989012-2.html b/layout/base/tests/bug989012-2.html
new file mode 100644
index 0000000000..7edb002ab2
--- /dev/null
+++ b/layout/base/tests/bug989012-2.html
@@ -0,0 +1,29 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <style>
+ span:before {
+ content: "IMAGE";
+ }
+ </style>
+ </head>
+ <body onload="start()">
+ <div onfocus="setTimeout(done, 0)" contenteditable>foo<span></span>bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ sel.collapse(div, 0);
+ // Press Right four times to set the caret right before "bar"
+ for (var i = 0; i < 4; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug989012-3-ref.html b/layout/base/tests/bug989012-3-ref.html
new file mode 100644
index 0000000000..9d637f536f
--- /dev/null
+++ b/layout/base/tests/bug989012-3-ref.html
@@ -0,0 +1,28 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <style>
+ img {
+ border: solid 1px red;
+ mid-width: 1em;
+ display: inline-block;
+ }
+ </style>
+ </head>
+ <body onload="start()">
+ <div onfocus="done()" contenteditable>foo<img>bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ // Set the caret right before "bar"
+ sel.collapse(div.lastChild, 0);
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/bug989012-3.html b/layout/base/tests/bug989012-3.html
new file mode 100644
index 0000000000..7448b052b9
--- /dev/null
+++ b/layout/base/tests/bug989012-3.html
@@ -0,0 +1,31 @@
+<html class="reftest-wait">
+ <head>
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <style>
+ img {
+ border: solid 1px red;
+ mid-width: 1em;
+ display: inline-block;
+ }
+ </style>
+ </head>
+ <body onload="start()">
+ <div onfocus="setTimeout(done, 0)" contenteditable>foo<img>bar</div>
+ <script>
+ var div = document.querySelector("div");
+ function start() {
+ div.focus();
+ }
+ function done() {
+ var sel = getSelection();
+ sel.collapse(div, 0);
+ // Press Right four times to set the caret right before "bar"
+ for (var i = 0; i < 4; ++i) {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ document.documentElement.removeAttribute("class");
+ }
+ </script>
+ </body>
+</html>
diff --git a/layout/base/tests/chrome/animated.gif b/layout/base/tests/chrome/animated.gif
new file mode 100644
index 0000000000..b2895487bd
--- /dev/null
+++ b/layout/base/tests/chrome/animated.gif
Binary files differ
diff --git a/layout/base/tests/chrome/blue-32x32.png b/layout/base/tests/chrome/blue-32x32.png
new file mode 100644
index 0000000000..deefd19b2a
--- /dev/null
+++ b/layout/base/tests/chrome/blue-32x32.png
Binary files differ
diff --git a/layout/base/tests/chrome/bug1041200_frame.html b/layout/base/tests/chrome/bug1041200_frame.html
new file mode 100644
index 0000000000..0030ec0edd
--- /dev/null
+++ b/layout/base/tests/chrome/bug1041200_frame.html
@@ -0,0 +1,2 @@
+<body onload='parent.childLoaded()' style='background:lime'>
+<p>Hello<p>Hello<p>Hello<p>Hello<p>Hello<p>
diff --git a/layout/base/tests/chrome/bug1041200_window.html b/layout/base/tests/chrome/bug1041200_window.html
new file mode 100644
index 0000000000..005f7bcd13
--- /dev/null
+++ b/layout/base/tests/chrome/bug1041200_window.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 1041200</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+</head>
+<body>
+<iframe style="width:700px; height:500px; margin-top:200px;" id="ourFrame"></iframe>
+<script>
+var SpecialPowers = window.arguments[0].SpecialPowers;
+var SimpleTest = window.arguments[0].SimpleTest;
+var ok = window.arguments[0].ok;
+var info = window.arguments[0].info;
+
+var viewer = SpecialPowers.setFullZoom(window, 2);
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function() {
+ window.waitForAllPaintsFlushed(function () {
+ // Supply random key to ensure load actually happens
+ ourFrame.src = "bug1041200_frame.html?" + Math.random();
+ }, document.getElementById("ourFrame").contentDocument);
+};
+
+window.childLoaded = function() {
+ setTimeout(function() {
+ window.waitForAllPaintsFlushed(function(x1, y1, x2, y2) {
+ // We set the full zoom of this window too, so we need to account for it here.
+ ok(x2 - x1 >= 700 / 2 && y2 - y1 >= 500 / 2,
+ "expected to see invalidate of entire frame, got " + [x1,y1,x2,y2].join(','));
+ SimpleTest.finish();
+ window.close();
+ });
+ }, 0);
+};
+</script>
+
diff --git a/layout/base/tests/chrome/bug1722890.html b/layout/base/tests/chrome/bug1722890.html
new file mode 100644
index 0000000000..76175eb33d
--- /dev/null
+++ b/layout/base/tests/chrome/bug1722890.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+div {
+ position: fixed;
+ width: 20px;
+ height: 20px;
+ background: blue;
+ left: 0;
+}
+ </style>
+ </head>
+ <body>
+ <div style="top:0"></div>
+ <div style="bottom:0"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/chrome/bug1722890_ref.html b/layout/base/tests/chrome/bug1722890_ref.html
new file mode 100644
index 0000000000..8bf8e39713
--- /dev/null
+++ b/layout/base/tests/chrome/bug1722890_ref.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <style>
+div {
+ position: fixed;
+ width: 10px;
+ height: 10px;
+ background: blue;
+ left: 0;
+}
+ </style>
+ </head>
+ <body>
+ <div style="top:0"></div>
+ <div style="bottom:0"></div>
+ </body>
+</html>
+
diff --git a/layout/base/tests/chrome/bug1769161_1.html b/layout/base/tests/chrome/bug1769161_1.html
new file mode 100644
index 0000000000..b1cc3b6ded
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_1.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 1in;
+ height: 1in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 20in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_1_ref.html b/layout/base/tests/chrome/bug1769161_1_ref.html
new file mode 100644
index 0000000000..ab56d8d045
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_1_ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 0.5in;
+ height: 0.5in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 10in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_2.html b/layout/base/tests/chrome/bug1769161_2.html
new file mode 100644
index 0000000000..7728d0bd95
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_2.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 2in;
+ height: 2in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 10in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_2_ref.html b/layout/base/tests/chrome/bug1769161_2_ref.html
new file mode 100644
index 0000000000..3916e26a17
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_2_ref.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 10in;
+ height: 10in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 50in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_3.html b/layout/base/tests/chrome/bug1769161_3.html
new file mode 100644
index 0000000000..b1cc3b6ded
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_3.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 1in;
+ height: 1in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 20in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_3_ref.html b/layout/base/tests/chrome/bug1769161_3_ref.html
new file mode 100644
index 0000000000..b786e1ad2b
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_3_ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 0.5in;
+ height: 0.5in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 10in;
+ margin: 2in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_4.html b/layout/base/tests/chrome/bug1769161_4.html
new file mode 100644
index 0000000000..7728d0bd95
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_4.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 2in;
+ height: 2in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 10in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug1769161_4_ref.html b/layout/base/tests/chrome/bug1769161_4_ref.html
new file mode 100644
index 0000000000..be2de41058
--- /dev/null
+++ b/layout/base/tests/chrome/bug1769161_4_ref.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<style>
+div {
+ width: 10in;
+ height: 10in;
+ background-color: blue;
+}
+body {
+ margin: 0;
+}
+@page {
+ size: 50in;
+ margin: 10in;
+}
+</style>
+<body>
+ <div></div>
+</body>
diff --git a/layout/base/tests/chrome/bug495648.rdf b/layout/base/tests/chrome/bug495648.rdf
new file mode 100644
index 0000000000..b7045aa70a
--- /dev/null
+++ b/layout/base/tests/chrome/bug495648.rdf
@@ -0,0 +1,214 @@
+<?xml version="1.0"?>
+<RDF:RDF xmlns:NS1="http://sitedelta.schierla.de/SD-rdf#"
+ xmlns:NC="http://home.netscape.com/NC-rdf#"
+ xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=19&amp;btnG=Suche&amp;meta="
+ NC:name="19 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Bag RDF:about="urn:root:bag">
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=1&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=2&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=3&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=4&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=5&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=6&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=7&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=8&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=9&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=10&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=11&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=12&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=13&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=14&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=15&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=16&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=17&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=18&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=19&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=20&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=21&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=22&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=23&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=24&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=25&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=26&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=27&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=28&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=29&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=30&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=31&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=32&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=33&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=34&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=35&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=36&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=37&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=38&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=39&amp;btnG=Suche&amp;meta="/>
+ <RDF:li RDF:resource="http://www.google.de/search?hl=de&amp;q=40&amp;btnG=Suche&amp;meta="/>
+ </RDF:Bag>
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=2&amp;btnG=Suche&amp;meta="
+ NC:name="2 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=12&amp;btnG=Suche&amp;meta="
+ NC:name="12 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=5&amp;btnG=Suche&amp;meta="
+ NC:name="5 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=6&amp;btnG=Suche&amp;meta="
+ NC:name="6 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=22&amp;btnG=Suche&amp;meta="
+ NC:name="22 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=17&amp;btnG=Suche&amp;meta="
+ NC:name="17 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="urn:root">
+ <NC:links RDF:resource="urn:root:bag"/>
+ </RDF:Description>
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=8&amp;btnG=Suche&amp;meta="
+ NC:name="8 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=1&amp;btnG=Suche&amp;meta="
+ NC:name="1 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=16&amp;btnG=Suche&amp;meta="
+ NC:name="16 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=21&amp;btnG=Suche&amp;meta="
+ NC:name="21 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=20&amp;btnG=Suche&amp;meta="
+ NC:name="20 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=9&amp;btnG=Suche&amp;meta="
+ NC:name="9 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=11&amp;btnG=Suche&amp;meta="
+ NC:name="11 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=14&amp;btnG=Suche&amp;meta="
+ NC:name="14 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=23&amp;btnG=Suche&amp;meta="
+ NC:name="23 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=24&amp;btnG=Suche&amp;meta="
+ NC:name="24 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=18&amp;btnG=Suche&amp;meta="
+ NC:name="18 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www0.pafnet.de/user/322033"
+ NC:name="pafnet - You2_xD"
+ NS1:nextScan="1243707104073"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=3&amp;btnG=Suche&amp;meta="
+ NC:name="3 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=7&amp;btnG=Suche&amp;meta="
+ NC:name="7 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=15&amp;btnG=Suche&amp;meta="
+ NC:name="15 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=4&amp;btnG=Suche&amp;meta="
+ NC:name="4 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=13&amp;btnG=Suche&amp;meta="
+ NC:name="13 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=10&amp;btnG=Suche&amp;meta="
+ NC:name="10 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=25&amp;btnG=Suche&amp;meta="
+ NC:name="25 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=26&amp;btnG=Suche&amp;meta="
+ NC:name="26 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=27&amp;btnG=Suche&amp;meta="
+ NC:name="27 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=28&amp;btnG=Suche&amp;meta="
+ NC:name="28 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=29&amp;btnG=Suche&amp;meta="
+ NC:name="29 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=30&amp;btnG=Suche&amp;meta="
+ NC:name="30 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=31&amp;btnG=Suche&amp;meta="
+ NC:name="31 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=32&amp;btnG=Suche&amp;meta="
+ NC:name="32 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=33&amp;btnG=Suche&amp;meta="
+ NC:name="33 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=34&amp;btnG=Suche&amp;meta="
+ NC:name="34 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=35&amp;btnG=Suche&amp;meta="
+ NC:name="35 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=36&amp;btnG=Suche&amp;meta="
+ NC:name="36 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=37&amp;btnG=Suche&amp;meta="
+ NC:name="37 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=38&amp;btnG=Suche&amp;meta="
+ NC:name="38 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=39&amp;btnG=Suche&amp;meta="
+ NC:name="39 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+ <RDF:Description RDF:about="http://www.google.de/search?hl=de&amp;q=40&amp;btnG=Suche&amp;meta="
+ NC:name="40 - Google-Suche"
+ NS1:nextScan="0"
+ NS1:status="0" />
+</RDF:RDF>
diff --git a/layout/base/tests/chrome/bug551434_childframe.html b/layout/base/tests/chrome/bug551434_childframe.html
new file mode 100644
index 0000000000..3d7bd6c13a
--- /dev/null
+++ b/layout/base/tests/chrome/bug551434_childframe.html
@@ -0,0 +1,4 @@
+<script>
+var gKeyDownChild = 0, gKeyPressChild = 0, gKeyUpChild = 0;
+</script>
+<input id='i4' onkeydown="gKeyDownChild++" onkeypress="gKeyPressChild++" onkeyup="gKeyUpChild++; this.parentNode.removeChild(this);">
diff --git a/layout/base/tests/chrome/chrome.ini b/layout/base/tests/chrome/chrome.ini
new file mode 100644
index 0000000000..13f65215c0
--- /dev/null
+++ b/layout/base/tests/chrome/chrome.ini
@@ -0,0 +1,138 @@
+[DEFAULT]
+prefs =
+ layout.css.constructable-stylesheets.enabled=true
+ layout.css.individual-transform.enabled=true
+ layout.css.motion-path.enabled=true
+ layout.css.page-size.enabled=true
+skip-if = os == 'android'
+support-files =
+ animated.gif
+ blue-32x32.png
+ bug1722890.html
+ bug1722890_ref.html
+ bug1769161_1.html
+ bug1769161_1_ref.html
+ bug1769161_2.html
+ bug1769161_2_ref.html
+ bug1769161_3.html
+ bug1769161_3_ref.html
+ bug1769161_4.html
+ bug1769161_4_ref.html
+ bug551434_childframe.html
+ chrome_content_integration_window.xhtml
+ default_background_window.xhtml
+ dialog_with_positioning_window.xhtml
+ file_bug458898.html
+ green.png
+ printpreview_bug1713404_ref.html
+ printpreview_bug1730091_ref.html
+ printpreview_bug396024_helper.xhtml
+ printpreview_bug482976_helper.xhtml
+ printpreview_helper.xhtml
+ printpreview_downloadable_font.html
+ printpreview_downloadable_font_ref.html
+ printpreview_downloadable_font_in_iframe.html
+ printpreview_downloadable_font_in_iframe_ref.html
+ printpreview_font_api.html
+ printpreview_font_api_ref.html
+ printpreview_font_mozprintcallback.html
+ printpreview_font_mozprintcallback_ref.html
+ printpreview_quirks.html
+ printpreview_quirks_ref.html
+ printpreview_images.html
+ printpreview_images_ref.html
+ printpreview_images_sw.html
+ printpreview_images_sw_ref.html
+ printpreview_images_sw.js
+ printpreview_image_select.html
+ printpreview_image_select_ref.html
+ printpreview_mixed_page_size_001.html
+ printpreview_mixed_page_size_002.html
+ printpreview_pps_uw2.html
+ printpreview_pps_uw2_ref.html
+ printpreview_pps_uw2_no_margin_ref.html
+ printpreview_pps_uw4.html
+ printpreview_pps_uw4_ref.html
+ printpreview_pps_uw9.html
+ printpreview_pps_uw9_ref.html
+ printpreview_pps2.html
+ printpreview_pps2_ref.html
+ printpreview_pps4.html
+ printpreview_pps4_ref.html
+ printpreview_pps6.html
+ printpreview_pps6_ref.html
+ printpreview_pps9.html
+ printpreview_pps9_ref.html
+ printpreview_pps16.html
+ printpreview_pps16_ref.html
+ printpreview_prettyprint.xml
+ printpreview_prettyprint_ref.xhtml
+ printpreview_mask.html
+ print_page_size1.html
+ print_page_size1_ref.html
+ print_page_size2.html
+ print_page_size2_ref.html
+ print_page_size3.html
+ print_page_size3_ref.html
+ print_page_size4.html
+ print_page_size4_ref.html
+ red.png
+ color_adjust.html
+ color_adjust_ref.html
+ test_document_adopted_styles.html
+ test_document_adopted_styles_ref.html
+ test_shadow_root_adopted_styles.html
+ test_shadow_root_adopted_styles_ref.html
+ test_shared_adopted_styles.html
+ test_shared_adopted_styles_ref.html
+ file_bug1018265.xhtml
+ markA.ttf
+ markB.ttf
+
+[test_bug396367-1.html]
+[test_bug396367-2.html]
+[test_bug420499.xhtml]
+[test_bug458898.html]
+[test_bug465448.xhtml]
+support-files =
+ file_bug465448.html
+[test_bug514660.xhtml]
+[test_bug533845.xhtml]
+skip-if = os == 'linux' && !debug # Bug 1208197
+[test_bug551434.html]
+[test_bug708062.html]
+[test_bug812817.xhtml]
+[test_bug1018265.xhtml]
+[test_bug1041200.xhtml]
+skip-if = os == 'win' && bits == 64 # Bug 1272321
+support-files =
+ bug1041200_frame.html
+ bug1041200_window.html
+[test_chrome_content_integration.xhtml]
+[test_color_scheme_browser.xhtml]
+[test_default_background.xhtml]
+[test_dialog_with_positioning.html]
+tags = openwindow
+[test_fixed_bg_scrolling_repaints.html]
+[test_prerendered_transforms.html]
+[test_printer_default_settings.html]
+[test_printpreview.xhtml]
+skip-if = (os == "linux" && bits == 32) || (verify && (os == 'win')) # Disabled on Linux32 for bug 1278957
+[test_printpreview_bug396024.xhtml]
+skip-if = (verify && (os == 'win'))
+[test_printpreview_bug482976.xhtml]
+skip-if = (verify && (os == 'win'))
+[test_scrolling_repaints.html]
+[test_will_change.html]
+skip-if = true
+[test_get_printer_basic_attributes.html]
+[test_get_printer_paper_sizes.html]
+[test_get_printer_orientation.html]
+[test_getClientRectsAndTexts.html]
+[test_css_visibility_propagation.xhtml]
+support-files =
+ window_css_visibility_propagation-1.xhtml
+ window_css_visibility_propagation-2.xhtml
+ window_css_visibility_propagation-3.html
+ window_css_visibility_propagation-4.html
+ frame_css_visibility_propagation.html
diff --git a/layout/base/tests/chrome/chrome_content_integration_window.xhtml b/layout/base/tests/chrome/chrome_content_integration_window.xhtml
new file mode 100644
index 0000000000..1dd1b7f882
--- /dev/null
+++ b/layout/base/tests/chrome/chrome_content_integration_window.xhtml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<window title="Content/chrome integration subwindow"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTests()"
+ style="background:black; -moz-appearance:none;">
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <stack style="align-items: center; height: 300px; width: 200px;">
+ <!-- the bottom 100px is a strip of black that should be visible through the content iframe -->
+ <vbox style="background:pink; border-bottom:100px solid black"/>
+ <!-- the middle 100px is a strip of black in the content iframe -->
+ <!-- the bottom 100px of the iframe is transparent, the top 100px is yellow -->
+ <iframe type="content" style="border:none; width: 10px; height: 10px;"
+ transparent="transparent"
+ src="data:text/html,&lt;div style='position:absolute;left:0;top:0;width:100%;height:100px;background:yellow;border-bottom:100px solid black'&gt;"/>
+ <!-- the top 100px is a strip of black above the content iframe -->
+ <vbox style="border-top:100px solid black;"/>
+ </stack>
+
+ <script type="application/javascript">
+ <![CDATA[
+ var imports = [ "SimpleTest", "is", "isnot", "ok", "SpecialPowers" ];
+ for (var name of imports) {
+ window[name] = window.arguments[0][name];
+ }
+
+ function runTests() {
+ var testCanvas = snapshotWindow(window);
+
+ var refCanvas = snapshotWindow(window);
+ var ctx = refCanvas.getContext('2d');
+ ctx.fillStyle = "black";
+ ctx.fillRect(0, 0, refCanvas.width, refCanvas.height);
+
+ var comparison = compareSnapshots(testCanvas, refCanvas, true);
+ ok(comparison[0], "Rendering OK, got " + comparison[1] + ", expected " + comparison[2]);
+
+ var tester = window.SimpleTest;
+ window.close();
+ tester.finish();
+ }
+ ]]>
+ </script>
+</window>
diff --git a/layout/base/tests/chrome/color_adjust.html b/layout/base/tests/chrome/color_adjust.html
new file mode 100644
index 0000000000..4a9846e28d
--- /dev/null
+++ b/layout/base/tests/chrome/color_adjust.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<style>
+.test {
+ background-color: black;
+ color: white;
+ color-adjust: exact;
+}
+</style>
+<span class="test">Some text goes here</span>
diff --git a/layout/base/tests/chrome/color_adjust_ref.html b/layout/base/tests/chrome/color_adjust_ref.html
new file mode 100644
index 0000000000..f5986f93bf
--- /dev/null
+++ b/layout/base/tests/chrome/color_adjust_ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<style>
+.test {
+ background-color: black;
+ color: white;
+}
+</style>
+<span class="test">Some text goes here</span>
diff --git a/layout/base/tests/chrome/default_background_window.xhtml b/layout/base/tests/chrome/default_background_window.xhtml
new file mode 100644
index 0000000000..4bc49c0f36
--- /dev/null
+++ b/layout/base/tests/chrome/default_background_window.xhtml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTests()">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js"></script>
+
+ <iframe type="content" id="f" src="about:blank" style="border:1px solid black;"/>
+
+ <script>
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+
+ var imports = [ "SimpleTest", "is", "isnot", "ok" ];
+ for (var name of imports) {
+ window[name] = window.arguments[0][name];
+ }
+
+ function snapshot(win) {
+ var el = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+ el.width = win.innerWidth;
+ el.height = win.innerHeight;
+
+ var ctx = el.getContext("2d");
+ ctx.drawWindow(win, 0, 0,
+ win.innerWidth, win.innerHeight,
+ "rgba(0,0,0,0)", 0);
+ return el;
+ }
+
+ var color = '#2468AC';
+ var prefs = Cc["@mozilla.org/preferences-service;1"]
+ .getService(Ci.nsIPrefBranch);
+ var backgroundPref = matchMedia('(prefers-color-scheme:dark)').matches
+ ? 'browser.display.background_color.dark'
+ : 'browser.display.background_color';
+ prefs.setCharPref(backgroundPref, color);
+ // On Windows, this preference is true by default, we make it
+ // false to ensure we're using the color set above for our reference.
+ prefs.setBoolPref('browser.display.use_system_colors', false);
+
+ function runTests() {
+ var f = document.getElementById("f");
+
+ var testCanvas = snapshot(f.contentWindow);
+ prefs.clearUserPref(backgroundPref);
+ // Reset sys colors pref so we're using the system color for our test.
+ prefs.clearUserPref('browser.display.use_system_colors');
+
+ var refCanvas = snapshot(f.contentWindow);
+ var ctx = refCanvas.getContext('2d');
+ ctx.fillStyle = color;
+ ctx.fillRect(0, 0, refCanvas.width, refCanvas.height);
+
+ var comparison = compareSnapshots(testCanvas, refCanvas, true);
+ ok(comparison[0], "Rendering OK, got " + comparison[1] + ", expected " + comparison[2]);
+
+
+ var tester = window.SimpleTest;
+ window.close();
+ tester.finish();
+ }
+ ]]>
+ </script>
+</window>
diff --git a/layout/base/tests/chrome/dialog_with_positioning_window.xhtml b/layout/base/tests/chrome/dialog_with_positioning_window.xhtml
new file mode 100644
index 0000000000..a854248b21
--- /dev/null
+++ b/layout/base/tests/chrome/dialog_with_positioning_window.xhtml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="setTimeout(runTest, 0)">
+ <vbox>
+ <label value="powered by example.com" style="padding: 16px;"/>
+ </vbox>
+ <hbox id="t" style="display: block; position: fixed; right: 16px; bottom: 16px;">
+ <button label="OK"/>
+ </hbox>
+<script><![CDATA[
+var SimpleTest = window.arguments[0].SimpleTest;
+var SpecialPowers = window.arguments[0].SpecialPowers;
+var is = window.arguments[0].is;
+var ok = window.arguments[0].ok;
+
+// We run this off a setTimeout from onload, because the XUL window
+// only does its intrinsic-height layout after the load event has
+// finished
+function runTest() {
+ var t = document.getElementById("t");
+ var tBottom = t.getBoundingClientRect().bottom;
+ is(tBottom, document.documentElement.getBoundingClientRect().bottom - 16,
+ "check fixed-pos element t bottom positioned correctly");
+ ok(tBottom < 200, "fixed-pos element t bottom must be sane, less than 200 (got " + tBottom + ")");
+ window.close();
+ SimpleTest.finish();
+}
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/file_bug1018265.xhtml b/layout/base/tests/chrome/file_bug1018265.xhtml
new file mode 100644
index 0000000000..a645c325df
--- /dev/null
+++ b/layout/base/tests/chrome/file_bug1018265.xhtml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1018265
+-->
+<window title="Mozilla Bug 1018265"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="setTimeout(run, 0);">
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 1018265 **/
+
+ var testcontent = null;
+
+ function run() {
+ testcontent = document.getElementById("testcontent");
+ shouldHaveTwoNonHiddenContentViewers();
+ testcontent.setAttribute("src", "foobarpage");
+ setTimeout(errorPageLoaded, 2500)
+ }
+
+ function errorPageLoaded() {
+ testcontent.addEventListener("pageshow", didGoBack, true);
+ setTimeout(function() {testcontent.contentWindow.history.back();}, 0);
+ }
+
+ function didGoBack(e) {
+ testcontent.removeEventListener("pageshow", didGoBack, true);
+ shouldHaveTwoNonHiddenContentViewers();
+ window.arguments[0].done();
+ window.close();
+ }
+
+ function getContentViewer(win) {
+ return win.docShell.contentViewer;
+ }
+
+ function shouldHaveTwoNonHiddenContentViewers() {
+ window.arguments[0].is(getContentViewer(testcontent.contentWindow).isHidden, false, "Top level ContentViewer should not be hidden.");
+ window.arguments[0].is(getContentViewer(testcontent.contentWindow.frames[0]).isHidden, false, " Iframe's ContentViewer should not be hidden.");
+ }
+ ]]>
+ </script>
+
+ <browser type="content" id="testcontent" flex="1" src="data:text/html,&lt;iframe&gt;&lt;/iframe&gt;"/>
+</window>
diff --git a/layout/base/tests/chrome/file_bug458898.html b/layout/base/tests/chrome/file_bug458898.html
new file mode 100644
index 0000000000..6db840689d
--- /dev/null
+++ b/layout/base/tests/chrome/file_bug458898.html
@@ -0,0 +1 @@
+<div style='height:200px; width:100px;'>
diff --git a/layout/base/tests/chrome/file_bug465448.html b/layout/base/tests/chrome/file_bug465448.html
new file mode 100644
index 0000000000..5df5b61375
--- /dev/null
+++ b/layout/base/tests/chrome/file_bug465448.html
@@ -0,0 +1 @@
+<body onload='window.opener.loaded()'><div style='height:200px; width:100px;'>
diff --git a/layout/base/tests/chrome/frame_css_visibility_propagation.html b/layout/base/tests/chrome/frame_css_visibility_propagation.html
new file mode 100644
index 0000000000..dbb5d819d1
--- /dev/null
+++ b/layout/base/tests/chrome/frame_css_visibility_propagation.html
@@ -0,0 +1 @@
+<button id="button">Button</button>
diff --git a/layout/base/tests/chrome/green.png b/layout/base/tests/chrome/green.png
new file mode 100644
index 0000000000..25b76c3c6f
--- /dev/null
+++ b/layout/base/tests/chrome/green.png
Binary files differ
diff --git a/layout/base/tests/chrome/markA.ttf b/layout/base/tests/chrome/markA.ttf
new file mode 100644
index 0000000000..353e7ac332
--- /dev/null
+++ b/layout/base/tests/chrome/markA.ttf
Binary files differ
diff --git a/layout/base/tests/chrome/markB.ttf b/layout/base/tests/chrome/markB.ttf
new file mode 100644
index 0000000000..c683ddf945
--- /dev/null
+++ b/layout/base/tests/chrome/markB.ttf
Binary files differ
diff --git a/layout/base/tests/chrome/print_page_size1.html b/layout/base/tests/chrome/print_page_size1.html
new file mode 100644
index 0000000000..851b910f26
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 3in;
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 20px;
+ height: 20px;
+}
+.upper {
+ top: 1in;
+}
+.lower{
+ bottom: 1.5in;
+}
+.left{
+ left: 1in;
+}
+.right{
+ right: 1.5in;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size1_ref.html b/layout/base/tests/chrome/print_page_size1_ref.html
new file mode 100644
index 0000000000..cc3ff8598f
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size1_ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 2in;
+ margin: 0.5in;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 20px;
+ height: 20px;
+}
+.upper {
+ top: 0.5in;
+}
+.lower{
+ bottom: 0;
+}
+.left{
+ left: 0.5in;
+}
+.right{
+ right: 0;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size2.html b/layout/base/tests/chrome/print_page_size2.html
new file mode 100644
index 0000000000..d9a181576d
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size2.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 20in 3in;
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 20px;
+ height: 20px;
+}
+.upper {
+ top: 0;
+}
+.lower{
+ bottom: 0;
+}
+.left{
+ left: 0;
+}
+.right{
+ right: 0;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size2_ref.html b/layout/base/tests/chrome/print_page_size2_ref.html
new file mode 100644
index 0000000000..0a40acc0eb
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size2_ref.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 20in 5in;
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 20px;
+ height: 20px;
+}
+.upper {
+ top: 0;
+}
+.lower{
+ /* This accounts for the difference in @page{ size: ... }, as long as the
+ * page is placed aligned to the upper left corner of the paper.
+ */
+ bottom: 2in;
+}
+.left{
+ left: 0;
+}
+.right{
+ right: 0;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size3.html b/layout/base/tests/chrome/print_page_size3.html
new file mode 100644
index 0000000000..45c9709817
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size3.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 3in;
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 1in;
+ height: 1in;
+}
+.upper {
+ top: 0in;
+}
+.lower{
+ bottom: 0in;
+}
+.left{
+ left: 0in;
+}
+.right{
+ right: 0in;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size3_ref.html b/layout/base/tests/chrome/print_page_size3_ref.html
new file mode 100644
index 0000000000..cc6f69a7ad
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size3_ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<style>
+@page {
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 1in;
+ height: 1in;
+}
+/* These positions assume the test case was placed in the upper left corner of
+ * the page, rather than being centered or scaled.
+ */
+.upper {
+ top: 0;
+}
+.lower{
+ top: 2in;
+}
+.left{
+ left: 0;
+}
+.right{
+ left: 2in;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size4.html b/layout/base/tests/chrome/print_page_size4.html
new file mode 100644
index 0000000000..ef6077e2a1
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size4.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 10in;
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 1in;
+ height: 1in;
+}
+.upper {
+ top: 0;
+}
+.lower{
+ bottom: 0;
+}
+.left{
+ left: 0;
+}
+.right{
+ right: 0;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/print_page_size4_ref.html b/layout/base/tests/chrome/print_page_size4_ref.html
new file mode 100644
index 0000000000..3d32134824
--- /dev/null
+++ b/layout/base/tests/chrome/print_page_size4_ref.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<style>
+@page {
+ size: 20in;
+ margin: 0;
+}
+div {
+ position: absolute;
+ background: blue;
+ width: 2in;
+ height: 2in;
+}
+.upper {
+ top: 0;
+}
+.lower{
+ bottom: 0;
+}
+.left{
+ left: 0;
+}
+.right{
+ right: 0;
+}
+</style>
+<body>
+<div class="upper left"></div>
+<div class="upper right"></div>
+<div class="lower left"></div>
+<div class="lower right"></div>
+</body>
diff --git a/layout/base/tests/chrome/printpreview_bug1713404_ref.html b/layout/base/tests/chrome/printpreview_bug1713404_ref.html
new file mode 100644
index 0000000000..a08074b8a2
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_bug1713404_ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<style>
+@page {
+ margin: 2in;
+}
+div {
+ /* The default header/footer size in Firefox. */
+ font-size: 10pt;
+ position: absolute;
+}
+.upper {
+ top: 0;
+}
+.lower{
+ bottom: 0;
+}
+.left{
+ left: 0;
+}
+.right{
+ right: 0;
+}
+</style>
+<div class="upper left">|</div>
+<div class="upper right">||</div>
+<div class="lower left">|||</div>
+<div class="lower right">||||</div>
diff --git a/layout/base/tests/chrome/printpreview_bug1730091_ref.html b/layout/base/tests/chrome/printpreview_bug1730091_ref.html
new file mode 100644
index 0000000000..ac1577b852
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_bug1730091_ref.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<style>
+@page{
+ margin: 0;
+}
+div {
+ /* The default header/footer size in Firefox */
+ font-size: 10pt;
+ position: absolute;
+}
+.upper {
+ top: 0;
+}
+.lower{
+ bottom: 0;
+}
+.left{
+ left: 0;
+}
+.right{
+ right: 0;
+}
+</style>
+<div class="upper left">||||</div>
+<div class="upper right">||||</div>
+<div class="lower left">||||</div>
+<div class="lower right">||||</div>
diff --git a/layout/base/tests/chrome/printpreview_bug396024_helper.xhtml b/layout/base/tests/chrome/printpreview_bug396024_helper.xhtml
new file mode 100644
index 0000000000..404e689ef7
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_bug396024_helper.xhtml
@@ -0,0 +1,112 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396024
+-->
+<window title="Mozilla Bug 396024" onload="run()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<iframe id="i" src="about:blank" type="content"></iframe>
+<iframe src="about:blank" type="content"></iframe>
+<script type="application/javascript">
+<![CDATA[
+// Note: We can't use window.frames directly here because the type="content"
+// attributes isolate the frames into their own BrowsingContext hierarchies.
+let frameElts = document.getElementsByTagName("iframe");
+
+var is = window.arguments[0].is;
+var ok = window.arguments[0].ok;
+var todo = window.arguments[0].todo;
+var SimpleTest = window.arguments[0].SimpleTest;
+var gWbp;
+function printpreview() {
+ gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
+ var listener = {
+ onLocationChange: function(webProgress, request, location, flags) { },
+ onProgressChange: function(webProgress, request, curSelfProgress,
+ maxSelfProgress, curTotalProgress,
+ maxTotalProgress) { },
+ onSecurityChange: function(webProgress, request, state) { },
+ onStateChange: function(webProgress, request, stateFlags, status) { },
+ onStatusChange: function(webProgress, request, status, message) { },
+ onContentBlockingEvent: function(webProgress, request, event) { },
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWebProgressListener) ||
+ iid.equals(Ci.nsISupportsWeakReference))
+ return this;
+ throw Components.Exception("", Cr.NS_NOINTERFACE);
+ }
+ }
+ let settings = Cc["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Ci.nsIPrintSettingsService).createNewPrintSettings();
+ gWbp.printPreview(settings, frameElts[0].contentWindow, listener);
+}
+
+function exitprintpreview() {
+ frameElts[1].contentWindow.docShell.exitPrintPreview();
+}
+
+function finish() {
+ SimpleTest.finish();
+ window.close();
+}
+
+function run()
+{
+/** Test for Bug 396024 **/
+ var printService = Cc["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Ci.nsIPrintSettingsService);
+
+ if (printService.lastUsedPrinterName != '') {
+ printpreview();
+ ok(gWbp.doingPrintPreview, "Should be doing print preview");
+ exitprintpreview();
+ ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore1");
+ printpreview();
+ setTimeout(run2, 0)
+ } else {
+ todo(false, "No printer seems installed on this machine, that is necessary for this test");
+ finish();
+ }
+}
+
+function run2() {
+ var loadhandler = function() {
+ document.getElementById("i").removeEventListener("load", arguments.callee, true);
+ setTimeout(run3, 0);
+ };
+ document.getElementById("i").addEventListener("load", loadhandler, true);
+ frameElts[0].contentWindow.location.reload();
+}
+
+function run3() {
+ gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
+ ok(gWbp.doingPrintPreview, "Should be doing print preview");
+ exitprintpreview();
+ setTimeout(run4, 0);
+}
+
+function run4() {
+ var i = document.getElementById("i");
+ i.remove();
+ var loadhandler = function() {
+ document.getElementById("i").removeEventListener("load", loadhandler, true);
+ setTimeout(run5, 0);
+ };
+ i.addEventListener("load", loadhandler, true);
+ document.documentElement.getBoundingClientRect();
+ document.documentElement.prepend(i);
+}
+
+function run5() {
+ gWbp = frameElts[1].contentWindow.docShell.initOrReusePrintPreviewViewer();
+ ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore2");
+
+ //XXX this shouldn't be necessary, see bug 405555
+ printpreview();
+ exitprintpreview();
+ finish(); //should not have crashed after all of this
+}
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml b/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml
new file mode 100644
index 0000000000..cd23c90d7b
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_bug482976_helper.xhtml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=482976
+-->
+<window title="Mozilla Bug 482976" onload="run1()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<iframe src="about:blank" type="content"></iframe>
+<iframe src="about:blank" type="content"></iframe>
+<script type="application/javascript">
+<![CDATA[
+// Note: We can't use window.frames directly here because the type="content"
+// attributes isolate the frames into their own BrowsingContext hierarchies.
+let frameElts = document.getElementsByTagName("iframe");
+
+var is = window.arguments[0].is;
+var ok = window.arguments[0].ok;
+var todo = window.arguments[0].todo;
+var SimpleTest = window.arguments[0].SimpleTest;
+var gWbp;
+var gPrintPreviewWin;
+function printpreview() {
+ var listener = {
+ onLocationChange: function(webProgress, request, location, flags) { },
+ onProgressChange: function(webProgress, request, curSelfProgress,
+ maxSelfProgress, curTotalProgress,
+ maxTotalProgress) { },
+ onSecurityChange: function(webProgress, request, state) { },
+ onStateChange: function(webProgress, request, stateFlags, status) { },
+ onStatusChange: function(webProgress, request, status, message) { },
+ onContentBlockingEvent: function(webProgress, request, event) { },
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWebProgressListener) ||
+ iid.equals(Ci.nsISupportsWeakReference))
+ return this;
+ throw Components.Exception("", Cr.NS_NOINTERFACE);
+ }
+ }
+
+ let settings = Cc["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Ci.nsIPrintSettingsService).createNewPrintSettings();
+
+ gPrintPreviewWin = frameElts[0].contentWindow.printPreview(settings, listener);
+ gWbp = gPrintPreviewWin.docShell.contentViewer;
+ gWbp.QueryInterface(Ci.nsIWebBrowserPrint);
+}
+
+function exitprintpreview() {
+ gPrintPreviewWin.docShell.exitPrintPreview();
+ gPrintPreviewWin.close();
+}
+
+function finish() {
+ SimpleTest.finish();
+ window.close();
+}
+
+async function run1() {
+ printpreview();
+ ok(gWbp.doingPrintPreview, "Should be doing print preview");
+ exitprintpreview();
+ ok(!gWbp.doingPrintPreview, "Should not be doing print preview anymore");
+ finish();
+}
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/printpreview_downloadable_font.html b/layout/base/tests/chrome/printpreview_downloadable_font.html
new file mode 100644
index 0000000000..6e30c55f79
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_downloadable_font.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-face-rule">
+<style type="text/css">
+
+@font-face {
+ font-family: "MarkA";
+ src: url(markA.ttf);
+}
+
+body { font-family: "MarkA"; }
+
+</style>
+</head>
+<body>
+
+<p>A</p>
+
+</body>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_downloadable_font_in_iframe.html b/layout/base/tests/chrome/printpreview_downloadable_font_in_iframe.html
new file mode 100644
index 0000000000..edee6ecca2
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_downloadable_font_in_iframe.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<link rel="help" href="https://drafts.csswg.org/css-fonts/#font-face-rule">
+</head>
+<iframe style="width:300px; height: 300px; border: 0"
+ srcdoc="<!DOCTYPE HTML>
+ <html>
+ <style>
+ @font-face {
+ font-family: 'MarkA';
+ src: url(markA.ttf);
+ }
+
+ body { font-family: 'MarkA'; }
+ </style>
+ <body>
+ <p>A</p>
+ </body>
+ </html>">
+</iframe>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_downloadable_font_in_iframe_ref.html b/layout/base/tests/chrome/printpreview_downloadable_font_in_iframe_ref.html
new file mode 100644
index 0000000000..0a2a50c665
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_downloadable_font_in_iframe_ref.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+</head>
+<iframe style="width:300px; height: 300px; border: 0"
+ srcdoc="<!DOCTYPE HTML>
+ <html>
+ <style>
+ @font-face {
+ font-family: 'MarkB';
+ src: url(markB.ttf);
+ }
+
+ body { font-family: 'MarkB'; }
+ </style>
+ <body>
+ <p>B</p>
+ </body>
+ </html>">
+</iframe>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_downloadable_font_ref.html b/layout/base/tests/chrome/printpreview_downloadable_font_ref.html
new file mode 100644
index 0000000000..5967abb5ba
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_downloadable_font_ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
+ "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en-US">
+<head>
+<title></title>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<style type="text/css">
+
+@font-face {
+ font-family: "MarkB";
+ src: url(markB.ttf);
+}
+
+body { font-family: "MarkB"; }
+
+</style>
+</head>
+<body>
+
+<p>B</p>
+
+</body>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_font_api.html b/layout/base/tests/chrome/printpreview_font_api.html
new file mode 100644
index 0000000000..1f82d5f2d9
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_font_api.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <style type="text/css">
+ .test {
+ font-family: test, monospace;
+ }
+ </style>
+</head>
+<body>
+ <p class="test">lmnop</p>
+ <script>
+ const fontData = "data:font/opentype;base64,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";
+
+ let testFontFace = new FontFace('test', 'url(' + fontData + ')');
+ document.fonts.add(testFontFace);
+ testFontFace.loaded.then(() => {
+ window.postMessage("ready", "*");
+ }).catch((e) => {
+ window.postMessage("error", "*");
+ });
+ </script>
+</body>
+</html> \ No newline at end of file
diff --git a/layout/base/tests/chrome/printpreview_font_api_ref.html b/layout/base/tests/chrome/printpreview_font_api_ref.html
new file mode 100644
index 0000000000..61b052b4a6
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_font_api_ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <style type="text/css">
+ @font-face {
+ font-family: test;
+ src: url(data:font/opentype;base64,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);
+ }
+ .test {
+ /*
+ * Intentionally use a different fallback font than the test file so that
+ * if the font fails to load the test and reference will still be
+ * different.
+ */
+ font-family: test, sans-serif;
+ }
+ </style>
+</head>
+<body>
+ <p class="test">lmnop</p>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_font_mozprintcallback.html b/layout/base/tests/chrome/printpreview_font_mozprintcallback.html
new file mode 100644
index 0000000000..1b4296e90a
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_font_mozprintcallback.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+</head>
+<body>
+ <canvas id="canvas" width="200" height="200"></canvas>
+ <script>
+ const fontData = "data:font/opentype;base64,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";
+ let canvas = document.getElementById('canvas');
+ canvas.mozPrintCallback = (obj) => {
+ let testFontFace = new FontFace('test', 'url(' + fontData + ')');
+ document.fonts.add(testFontFace);
+ testFontFace.load().then(() => {
+ let ctx = obj.context;
+ ctx.font = '10px test, monospace';
+ ctx.fillText("lmnop", 20, 20);
+ obj.done();
+ window.postMessage("ready", "*");
+ }).catch((e) => {
+ obj.done();
+ window.postMessage("error", "*");
+ });
+ };
+ </script>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_font_mozprintcallback_ref.html b/layout/base/tests/chrome/printpreview_font_mozprintcallback_ref.html
new file mode 100644
index 0000000000..e4dd82bc58
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_font_mozprintcallback_ref.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <style type="text/css">
+ @font-face {
+ font-family: test;
+ src: url(data:font/opentype;base64,T1RUTwAJAIAAAwAQQ0ZGICjPfaIAAACcAAAgsU9TLzJlXAb2AAAhUAAAAGBjbWFwiDMtcQAAIbAAAAMoaGVhZKspT7wAACTYAAAANmhoZWEEjgGGAAAlEAAAACRobXR4Mq4AAAAAJTQAAABsbWF4cAAbUAAAACWgAAAABm5hbWUqpGR/AAAlqAAAAmRwb3N0//OzMwAAKAwAAAAgAQAEBAABAQEeTldCSlpMK05pbWJ1c1JvbU5vOUwtTWVkaUl0YWwAAQEBRPgbAPgcAfgdAvgeA/gfBB4KAB+Lix4KAB+LiwwH+1z72Pp4+lgFHQAAANkPHQAAAAAQHQAAAQ4RHQAAACAdAAAfthIABQEBDSA9WmBWZXJzaW9uIDAuMTFTZWUgb3JpZ2luYWwgbm90aWNlTldCSlpMK05pbWJ1c1JvbU5vOUwtTWVkaUl0YWxOV0JKWkwrTmltYnVzUm9tTm85TC1NZWRpSXRhbE1lZGl1bQAAAAAAJAAlACgALAA0ADUAQgBDAEQARQBGAEgASgBLAE0ATgBPAFAAUQBTAFQAVQBWAFgAWgAAABsCAAEAAwEWAhgDmQUYBpsHQAh9CZoKiQwADO8PEhAOERkR5hPoFVkWCReIGGcZvhqCG9kdEh5mHm6LDhwCmxwAIBYcAoUcAq0VHP/iBhz/8hz/6hz/9xz/+Rz/8RwAAAgc//gcAAAc//EcAAMc//EcAAUIHP/VHAANHP/RHAAIHP/XHAAACBz/IRz/Rxz/Nhz/Dh8c/2UcAG0c/5gcAKMeHABHHAAAHABCHAAWHAA5HAArCBwAHhwAFhwAEBwAEhwAIBwALAgc/+IcABYFHP/OHP/FHP/kHP/pHP/WHP/uCBz/5Bz/9Bz/4Rz/+hz/4RwAAAgc/6Ic/8gcAEEcAG0fHAAAHACSHABIHACtHABbHABMCBwAJRwAHhwAJxwAEBwAKRwAAAgcAFEcADIc/8Qc/54fHAAAHP/zHP//HP/3HP/+HP/zCBwAIBz/+gUOHALSHP/SFhwAjBwChBUcAA4c//4cAA0c//4cAAQc//8IHAAgHP/7HAAKHP/4HAAAHP/oCBwAABz/9hz//Bz/7xz/+Bz/4Agc/4cc/kIFHP/xHP/NHP/wHP/xHP/PHP/6CBz/5wccAR4GHAD/HAC+HACpHADjHxwAqxz/kBwAZhz/Qx4c/t4GHADgHP+7FRwABxwAGxwAEBwACxwAIBwAAAgcAG8cADYc/8Uc/4YfHAAAHP+OHP/eHP+JHP/KHP+zCBz/yxz/tBz/uRz/2xz/oRwAAAgc/9sc/+8cAAscABcfHAAAHAAMHAADHAAQHAAJHAAaCBwAAhwAARwAAhwAAB4OHALSHAAVFhwCrRwBShUc/uEGHP/mBxwAKxz//BwACBz//hwACxz/+ggcAAYc//wcAAYc//UcAAAc//cIHAAAHP/mHP/8HP/vHP/lHP+kCBz/6Rz/shwAABwAABz//Bz/+wgc//Ic/+8c/9cc//Mc/9kcAAAIHP+WHP/GHABCHAB6HxwAABwAlRwARBwApxwAWxwATQgcACYcACAcACscABAcAC0cAAAIHAAvHAAAHAAnHP/tHAAWHP/gCBwAFhz/3xwABxz/4xwAAhz/vAgcAB0c//wFHAAzHADdBRz/4QYc//Uc/+oc//Uc//gc/+wcAAAIHP/3HAAAHP/4HAACHP/tHAAHCBz/0xwADxz/3xwABhz/0hwAAAgc/7QcAAAc/7kc/+4c/8Mc/90IHP98HP+zHP+pHP9pHAAAHP9nCBz/YRwAeRz/khwArx4cAE0cAAAcAGQcABQcADkcABoIHAAcHAANBRwAMBwAtQUcABMcAEYcAAccAAgcADUcAAQIDhwCmxz/6xYcAZ0cAY4VHADYHADGBRwAKxwAJxwACBwAAxwAKxwABggcABkHHP8tBhz/5wccAAkc//8cAAkc//8cAAMcAAAIHAAZHP/9HAAKHP/5HAAAHP/xCBwAABz/4xz/vBz/vBz/Mhz/UAgcAD4cAOQFHAAQHAA0HAATHAAPHAA7HAAFCBwAGQcc/soGHP/nBxwADhz//hwADBz//hwABRz//wgcAB8c//wcAAsc//ccAAAc/+oIHAAAHP/1HP/8HP/rHP/5HP/mCBz/hhz+PwUc//Ac/8sc//Ec//Mc/88c//oIHP/nBxwBIQYcABkHHP/MHAAEHP/yHAAJHAAAHAAhCBwAABwABhwAARwABhwAARwABQgcAEMcAPkFHABxHP8OBRwACRz/7BwABBz/9BwAABz/9QgcAAAc//Ic//Qc//gc/+Yc//4IHP/8HP//HP/1HP//HP/0HP//CBz/5wccARgGHAAZBxz/2hwABBz/8hwABxz/9RwAGAgOHAIsHAACFhwAABz/7hUcAB4GHAAMHAAdHAAHHAAGHAATHAAACBwAChwAABwADRz//RwAFxz/+AgcADEc/+8cACIc//kcACgcAAAIHACMHABZHABPHAB8HxwAABwAVhz/0hwAQxz/hhwAXQgc/8IcAC8c//EcABYcAAAcAC0IHABAHAAoHAApHABAHhwAUBwAABwAJxz/zxwADRz/iwgcABsc//wFHAAoHADJBRz/4gYc//cc/+4c//Ic//gc/+scAAAIHP/3HAAAHP/vHAAEHP/nHAAHCBz/1RwADhz/5RwABRz/5BwAAAgc/44c/6kc/6wc/5IfHAAAHP/lHAAFHP/qHAAIHP/vCBwAFhz/1hwAKRz/1BwAOBz/1AgcAE4c/8QcACMc/9EcAAAc/9IIHAAAHP/qHP/5HP/oHP/0HP/qCBz/6Rz/2Rz/3Rz/7Rz/zxwAAAgc/8wcAAAc/9IcABkc/+ccACoIHP/tHAAfHP/4HAAeHP/8HAA8CBz/4xwAAgUOHAJjHAAyFhwCWBwCnRUc/dkGHP/aHP9VBRwAGRz/+QUcADQcAGQcAD8cACkcAGYcAAIIHP9sHP3nBRz/8hz/zRz/5hz/6xz/zRwAAAgc//IGHP/nBxwBSwYcABkHHP+6HAADHP/zHAAGHAAAHAAgCBwAABwADxwABBwAFRwABxwAGggcAIwcAfoFHABeHP//HAAnHP/VHAAFHP+RCBwAGxz//gUOHAH0HP/rFhwBxxwAfxUc/9cc/8kc//Ec//Ec//AcAAAIHP/5HP/7HAAGHAAJHxwAABwAGRwAChwAKhwAGBwAUQgcAEccAOwFHP+RHP/5BRz/7hz/xQUc//ccADMc/+ocABUc/9UcAAAIHP+FHP9qHP9AHP9kHxz/tBwAKxz/zRwAQR4cAD0cAAAcAC4cACUcADkcAF8IHP/1HP/ZHP/9HP/yHAAAHP/yCBz/2xwAHhz/4xwAJR4cAC8cAAAcAC8cACccADkcAFcIHP9DHAE0FRwAFhz//hwADxz/7hwAABz/5QgcAAAc/8Uc/94c/48c/9oc/7wIHP/lHP/QHP/iHP/lHP/lHAAACBz/5hz/7RwAFxwAHx8cAAAcADQcACEcAGQcACkcAEoIHAAeHAA1HAAhHAAfHAAbHP/+CA4cAfQc//IWHABaHAKCFRwAMRwADhz/+Rz/6R8cAAAc//Mc//Mc/8sc/+Yc/6UIHP+iHP65BRz/9xz/4Bz/9Rz/1BwAABz/+wgc/+QcAEkc/+AcAEEeHACjHACdHACsHACzHxwASRz/0hwAMxz/vh4c/80cAAAc/9wc/+gc/80c/70IHABaHAFIBRz/uhz/8xz/zxz/+Rz/pxz/9ggcAMcc/u0VHAAdHAAPHP/pHP/SHxwAABz/xRz/5Rz/oxz/3Bz/wggc/94c/8Qc/9kc/+Ec/9UcAAAIHP/uHP/zHAAMHAAPHxwAABwACRwAEhwAUhwACBwAIAgcAAscACccABccAD8cAA8cACMIHAAaHAA5HAAgHAAeHAAhHAAACA4cAbwc//sWHAFDHACNFRz/0Bz/vRz/4Rz/6Rz/1RwAAAgc/9Uc/+IcACQcADYfHAAAHAA+HAAaHABaHAAkHABBCBwAGhwALxwAHhwAGBwAHhwAAAgcAAwcAAoc//kc//YfHAAAHP/8HP/+HP/6HP/6HP/3CBz/9xz/8Bz//Bz/9RwAABz/9Agc/+IcABkc/+ocACEeHAAkHAAaHAAdHAApHxwANRz/0hwAJRz/vh4c/3Mc/3Ac/1wc/2AfHP+nHAA/HP/CHABaHhwALBwAABwAKxwAEBwAIhwAHQgcABocABYcABAcABMcACMcADIIDhwB9Bz/6xYcAcAcAIMVHP/lHP/THP/nHP/lHP/xHAAACBz/+xz/+hwABhwABh8cAAAcAAkcABYcAFccACMcAH8IHABvHAGVBRz/xBz/8hz/zxz/+Rz/nBz/+Agc/+UHHAAVBhwAGhwADxz/9hz/7h8cAAAc//cc//oc/+cc/+kc/6wIHP/uHP+8BRz/6BwAGBz/7RwACRz/4xwAAAgc/4Yc/2oc/z8c/2MfHP+4HAAtHP/LHAA9HhwAPxwAABwALhwAJBwAORwAXwgc//gc/9sc//0c//EcAAAc//EIHP/ZHAAWHP/pHAAmHhwAMRwAABwALRwAJhwANxwAWAgc/04cATEVHAAWHP//HAAPHP/rHAAAHP/kCBwAABz/1hz/1Bz/fBz/3hz/wQgc/+Qc/88c/+Mc/+cc/+McAAAIHP/nHAAAHP/uHAAcHAACHAAjCBwAAxwANhwAHxwAXBwAJBwAQQgcACAcADkcACQcACAcAB4c//4IDhwBvBwABRYcATgcAI4VHP/RHP+7HP/kHP/qHP/WHAAACBz/1Rz/6hwAHBwANh8cAAAcABAcAAIcAA0cAAQcABQIHABoHAAUHAAxHAAWHAAyHAAuCBwAIxwAIBwAExwAJhwAABwAIwgcADMc/9QcACQc/8MeHP9wHP9wHP9dHP9bHxz/rBwAQBz/wRwAVx4cAEwcAAAcADYcACccADwcAGMIHP86HABZFRwAJBwAihwALhwATRwAMBwAAAgcABMcAAkc//Qc/+ofHAAAHP+0HP/QHP+/HP+zHP/jCBz/+Rz//hz/8Rz/+xz/9Rz//AgOHAH0HP/MFhwCEhwBrRUc/5EGHP/fHAAYHP/hHAAJHP/SHAAACBz/hRz/nRz/rxz/mx8cAAAc/80cABoc/9wcADYc/+kIHP+vHP/SHP/xHP/yHAAAHP/bCBwAABz/4RwAEhz/7BwALBz/8Qgc/8Ic//Ec/+wc//gc/+kc/+0IHP/vHP/yHP/1HP/oHAAAHP/oCBz/vxwASRz/1xwAcx4cAIwcAGMcAD4cAFgfHAAAHAA8HP/YHAAiHP+THAAfCBz/yxwADwUc/+AcAAkc/+0cAA4cAAAcAA8IHAAQHAAPHAAUHAANHhwABRwAABwABxz//xwACBz//QgcAAsc//0cAAkc//8cAAscAAAIHAAsHAAAHAAsHAAMHAAnHAAWCBwAOhwAIRwAHxwAMxwAABwAPAgcAAAcABAc//8cAAoc//scABAIHABDBhz+lRz+hRUcAAsc//8cAE8c/+YcABQc//YIHAAcHP/zHAANHP/uHAAAHP/mCBz/1Rz/1Bz/5hz/tB4c/78c/9IcACAcAC4fHAAAHAAUHAAJHAAQHAAVHAAUCBwADBwADBwAHxwAEhwABxz//wgcAIwcAbUVHAAaHAATHP/nHP/eHxwAABz/3hz/9Bz/zhz/7xz/3Agc/+oc/9Ic/+cc/+oc/+IcAAAIHP/kHP/xHAAVHAAmHxwAABwAKBwAERwAPRwAFRwAJggcABMcACEcABUcABAcABocAAAIDhwBFhwAAhYcANYcAI0VHP/yHP/sBRz/5hz/2Rz/6Rz/6xz/8RwAAAgc//gc//kcAAccAAgfHAAAHAAGHAAGHAAiHAADHAAMCBwAWxwBTgUc/8oc//Qc/7oc//Yc/7Ic//oIHP/lBxwAKxwAEBz/+Bz/6x8cAAAc//gc//0c//Ec//wc/+8IHP/GHP8pBRz/+Bz/5Bz/+xz/5BwAABz/8wgc/9scABwc/+YcACkeHAA8HAAAHAAlHAAfHABGHABpCBz/1BwCLhUc/90c/+Ec/+Ec/90fHP/ZHAAdHP/iHAAlHhwAJhwAHxwAHhwAJR8cACUc/+AcAB8c/9seDhwBFhz/QxYcANwcAZcVHAAyHAAMHP/7HP/mHxwAABz/9Bz/+xz/5xz/+Rz/4wgc/6Qc/p0FHP/mHP+cHP/vHP/hHP/kHAAACBz/9Rz/8xwABxwABR8cAAAcAAIcAAEcAAIcAAEcAAIIHAALHAAQHAACHAAFHAAAHAALCBwAGxz/6hwAFhz/5R4c/+Yc/+kc/+gc/+MfHP/SHAArHP/hHAA+HhwAZBwAABwARRwATBwAKBwAmwgcAHIcAbYFHP/FHP/zHP/bHP/7HP+QHP/2CBwAsxwA+xUc/90c/+Ec/+Ec/90fHP/ZHAAdHP/iHAAlHhwAJhwAHxwAHhwAJR8cACUc/+EcAB8c/9oeDhwBFhwAAhYcANYcAI0VHP/YHP/FHP/rHP/rHP/vHAAACBz/+Bz/+RwABxwACB8cAAAcAA4cAAgcACIcAA4cADQIHACRHAILBRz/qhz/7xz/0Bz/+hz/tRz/+Qgc/+UHHAALHAABHAAGHAAAHAAEHAAACBwAGxwADxz/9hz/7h8cAAAc//Mc//Ic/8Ic/+wc/70IHP+2HP74BRz/6hz/tBz/9Bz/xhwAABz/6Agc/9wcABsc/+gcACgeHAA+HAAAHAAmHAAfHABFHABpCA4cAwoc//IWHAAwHAGXFRwACAYcACAcAA4c//gc/+4fHAAAHP/yHP/4HP/eHP/lHP+hCBz/vRz/EgUcAHkGHAA1HAC+HAAmHABWHAA4HABDCBwAFhwAGRwAHRwAFRwADRwAAAgcAAocAAkc//Yc//MfHAAAHP/xHP/0HP/WHP/fHP+aCBz/7xz/zBz/8xz/1hz/3Rz/jwgcAHgGHAA7HADAHAAGHAASHAAkHAA/CBwAKhwAShwAKBwAKhwAHhwAAAgcAAwcAAsc//Yc//UfHAAAHP/6HP/9HP/1HP/8HP/zCBz/0xz/fQUc/+oc/8Ec//Ic/8IcAAAc/+QIHP/XHAAYHP/qHAArHhwAPhwAABwAKBwAIRwAOhwAYggc/+ocAA0FHP/7HP/4HP/7HP/5HP/+HP/9CBz/6Rz/3Bz/6xz/6hz/8xwAAAgc//cc//kcAAccAAcfHAAAHAALHAAAHAAAHAAVHABDCBwALRwAhQUcAA8cACscAAgcACYcAAAcABoIHAAoHP/fHAAfHP/UHhz/vBwAABz/0hz/2Bz/rBz/fAgcABMcADIcAAccABocAAAcABwIHAAqHP/nHAAaHP/WHhz/5BwAABz/4hz/9Rz/5Bz/6Qgc/9sc/+Qc/+Ic/9oc/78c/5wIHABAHADHBRz/wBz/8Rz/6xz//Rz/jRz/9wgOHAIsHP/6FhwB3RwAhxUc/9gc/8Ic//Mc//Ic/+8cAAAIHP/4HP/6HAAHHAAKHxwAABwAChwABxwAFxwAEhwANggcACQcAG0FHAAQHAAuHAAKHAAtHAAAHAAYCBwAMBz/5hwAGxz/0R4c/9scAAAc/9wc//Ec/+Qc/+YIHP/cHP/dHP/tHP/oHP+9HP+dCBwAQBwAxgUc/8Ac//Ic/7Ac//Uc/8gc//4IHP/lBxwAKhz//xwADBz/+xwAABz/7QgcAAAc//Uc//Qc/9Ec/94c/4gIHP/lHP+gBRz/9Bz/0hz/+Rz/5xz/9hz/2wgcAHkGHAAvHACtHAAkHABXHAA+HABRCBwAFBwAGxwAHxwAFhwAEhwAAAgcAAwcAA0c//Uc//YfHAAAHP/9HP/+HP/4HP/9HP/2CBz/yRz/WgUc//Ac/9Ac//Qc/8ccAAAc/+YIHP/bHAAaHP/pHAAqHhwAOxwAABwAKhwAIhwAOhwAYQgOHAH0HP/9FhwBHxwBzhUc/20c/3Qc/2Ec/1gfHP+rHABDHP/BHABcHhwAlRwAiBwAmxwAqR8cAFgc/74cAD8c/6UeHP/3HP/jFRwAHRwAEhz/6hz/3B8cAAAc/7wc/+Ec/34c/+Ac/7oIHP/jHP/CHP/iHP/jHP/dHAAACBz/4hz/7RwAGBwAJh8cAAAcAE4cACUcAJIcACMcAD4IHAAaHAAuHAAdHAAXHAAgHAAACA4cAfQc/4gWHACPHAGXFRwALRz//hwACRz//BwAABz/7QgcAAAc//cc//cc/9gc//Mc/88IHP+cHP58BRz/8Bz/wxz/9Bz/8xz/1xwAAAgc//oGHP/lBxwA+QYcABsHHP/PHAABHP/wHAAIHAAAHAAYCBwAABwADBwACBwAIRwAEhwARAgcAAMcAAocAAMcAAscAAIcAAcIHAADHAANBRwAIxz/7hwADBz//BwAFRwAAAgcAIccAI4cALUcAK0fHABKHP/WHAAvHP++Hhz/xxwAABz/1Bz/4Bz/xRz/rQgcACYcAHMFHP+aHP/wHP/ZHP/6HP/MHP/6CBwBAxz/1RUcABoc//4cAA8c/+kc//4c/+AIHP/8HP+/HP/gHP+cHP/cHP+/CBz/4Rz/yhz/3xz/5Bz/3xwAAAgc/+oc/+8cABAcABQfHAAAHAAQHAAIHAAeHAAbHABbCBwAGBwAUhwAChwAHBwAEBwAGAgcABkcACccACEcABkcABoc//4IDhwBhRz/6xYcADAcAZcVHAAtHP/+HAAJHP/8HAAAHP/tCBwAABz/5xz/3hz/gBz/vBz/GwgcAHkGHAAPHAAuHAAOHAArHAAEHAAPCBwAIBwAZRwADxwAJhwAHRwAMAgcABocAC0cABYcABkcAA0cAAAIHAAEHAAAHAAFHP/8HAAHHP/3CBwADxz/7xwADRz/+BwAEBwAAAgcACIcABkcAB8cACofHAAoHP/pHAAaHP/eHhz/zRwAABz/1xz/zRz/sRz/YggcAEIcANEFHP/EHP/xHP/tHP/9HP+HHP/2CA4cAYUc/+0WHAFgHAHNFRz/4wYc//Uc/+0c//0c//4c//EcAAAIHP/3HAAAHP/3HAACHP/uHAAHCBz/5xwAChz/8xwAAxz/7BwAAAgc/68c/8sc/84c/7QfHAAAHP/KHAAOHP/jHABCHP+yCBwAJhz/0xwAEhz/3hwAABz/5wgc/+Ic/+cc/+gc/+AeHP/pHAAAHP/rHAALHP/wHAAUCBz/7BwAGRz/+BwAFxz/+BwANggc/+UcAAMFHP/qHP9aBRwAGwYcAAQcAAwcAAwcAAgcAA0cAAAIHAAHHAAAHAALHP/9HAANHP/7CBwAGBz/+BwAExz//BwAFBwAAAgcAFQcAEAcADkcAEsfHAAAHAAtHP/nHAAxHP/IHABBCBz/2hwALRz/7hwAHxwAABwAFggcACAcABUcABUcACAeHAAuHAAAHAAaHP/cHAAQHP+tCBwAGxz//gUOHAEWHP/1FhwBJBwBwRUc/7gGHAAnHACRBRz/3QYc/8oc/68c/84c/9cc/7Ec/+IIHP/dBxwAMwYc/7wc/xIFHP/yHP/NHP/2HP/THAAAHP/vCBz/2RwAHBz/5hwAKh4cADwcAAAcACgcACEcAEIcAGcIHP/qHAAOBRz/3hz/yxz/5hz/5Rz/8BwAAAgc//gc//gcAAgcAAcfHAAAHAASHAASHABJHAAkHAB6CBwABRwAERwACxwAKBwAERwAPQgcAFMGDhwCLBwADxYcAcgcAIUVHP/dHP/LHP/sHP/rHP/vHAAACBz/+Bz/+hwACBwACR8cAAAcAAocAAAcAAAcABkcAF8IHABOHAEMBRz/igYc/8gc/z8c/+Mc/74c/8cc/7gIHP/hHP/aHP/qHP/vHP/sHAAACBz/8hz/+RwACBwAER8cAAAcAA8cAAIcAAgcAA0cACsIHABeHAE0BRz/+hz//xz/+hz//xz/8Bz//Qgc/7Yc//Mc/8Uc//gc/9Ac//4IHP/lBxwALhz//RwAChz/+xwAABz/7AgcAAAc//Ac//sc/+Qc//cc/+MIHP/YHP97BRz/8hz/0Bz/+Rz/3BwAABz/6Agc/88cABoc/+ccADEeHABDHAAAHAAeHAAaHABnHACQCBz/7hz/yRz/+hz/5RwAABz/5Agc/9gcABUc/+wcACweHAA8HAAAHAAxHAAoHAA0HABZCA4cApscABAWHAGXBBwAGRwAABwACRz//xwACBz/+QgcABQc/+4cAA4c/6McAAMc/3YIHAACHP+yBRwAABz//BwAABz//xz//xz/3Qgc//8c/9MFHAAbBhwAORwAVgUcAAYcAAgcACwcAE0cACwcAE8IHAAJHAAQHAACHAAEHAAIHAAPCBwAFxz+4wUcABsGHAC5HADEHABXHACFHAAAHABUCBwAIRz/4xwAHRz/4B4c/+Ic/+Yc/+Qc/+AfHAAAHP/yHAAGHP/yHAAPHP/rCBwADhz/7RwABhz/8xwAABz/9ggcAAAc/+Ic/+Ic/9Ic/6cc/5cIHP/gHAFMBRz/5QYc/6wc/28c//Ac/+Mc/8gc/5sIHP/9HAB9HP/5HABAHP/pHABWCBz/1xz/9xz/4Rz/+xz/rRz/8ggOHAG8HP+iFhwAbBwBmxUcABIcAAAcAAUc//8cAAcc//wIHAARHP/2HAAQHP/XHAAPHP+5CBwAIRz/YBwAERz/nxwAABz/4QgcAAAc/+kc//cc/+gc/+4c/+cIHP/rHP/kHP/lHP/rHP/vHAAACBz/+RwAABz/7RwABxz/+BwABwgc/+8cAAwc/+ccAAkc/+4cAAAIHP/mHP/oHP/mHP/jHxz/3xwAGxz/5RwAIx4cADccAAAcAEEcACgcADccAEIIHACCHACfHAB3HADtHAAAHABlCBwAIhz/4xwAHhz/3x4c/+Ic/+Uc/+Uc/+EfHAAAHP/oHAAHHP/0HAAYHP/uCBwAEhz/8hwABhz/+BwAABz/8wgcAAAc/+Ic/+4c/9Yc/7sc/3sIHP/0HABLBRz/6hwAdhz/5hwAcBz/7hwALwgc/9Ac//Qc/9wc//oc/78c//oIDhwA+hwAfRYOHgoDliX/DAmmCvcMC6aRjpKWlZSdDAyLDA4dAAAAIBMAawEBAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj9AQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVpbXF1eX2BhYmNkZWZnaGlqa2wLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwAAAAAAAwIkAfQABQAAAooCuwAAAIwCigK7AAAB3wAxAQIAAAAABgAAAAAAAACAAACvUAAgSgAAAAAAAAAAKjIxKgABACD7BAPE/rwAZAPEAUQAAAAAAAAAAAHOArAAAAAgAAMAAAABAAMAAQAAAAwABAMcAAAATgBAAAUADgB+AKwA/wExAUIBUwFhAXgBfgGSAscC3QPAIBQgGiAeICIgJiAwIDogRCCsISIhJiICIgYiDyISIhoiHiIrIkgiYCJlJcrgBva++wT//wAAACAAoQCuATEBQQFSAWABeAF9AZICxgLYA8AgEyAYIBwgICAmIDAgOSBEIKwhIiEmIgIiBiIPIhEiGiIeIisiSCJgImQlyuAA9r77AP//AAAAAAAA/s8AAAAAAAD+iAAA/m4AAAAA/EAAAAAAAAAAAN/a39AAAN+831Te3t7a3f7d+t3xAADd5t3i3dXduN2gAADaNgAACUIAAAABAE4BCgEgAAABwAHCAcQAAAHEAAABxAHGAAABzgHQAdQB2AAAAAAB2AAAAAAAAAAAAAAAAAAAAcwAAAAAAAAAAAAAAcQAAAHEAAABzgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAIAAAAAAAMAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAUABgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHAAgACQAKAAsAAAAMAAAADQAOAAAADwAQABEAEgATAAAAFAAVABYAFwAAABgAAAAZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAAXw889QAAA+gAAAAAngt+JwAAAACeC34nAAD+vA//A8QAAgARAAAAAAAAAAAAAQAAA8T+vAAA//8AAAAAAAACsADHAAAAAAAAAAAAAAAAABsAAAAAApsAAALSAAAC0gAAApsAAAIsAAACYwAAAfQAAAH0AAABvAAAAfQAAAG8AAAB9AAAARYAAAEWAAABFgAAAwoAAAIsAAAB9AAAAfQAAAGFAAABhQAAARYAAAIsAAACmwAAAbwAAAD6AAAAAFAAABsAAAAAABQA9gABAAAAAAAAABAAAAABAAAAAAABAB0AEAABAAAAAAACAAcALQABAAAAAAADAAgANAABAAAAAAAEAB0APAABAAAAAAAFAAwAWQABAAAAAAAGAAAAZQABAAAAAAAHAAcAZQABAAAAAAAIAAcAbAABAAAAAAAJAAcAcwADAAEECQAAACAAegADAAEECQABADoAmgADAAEECQACAA4A1AADAAEECQADABAA4gADAAEECQAEADoA8gADAAEECQAFABgBLAADAAEECQAGAAABRAADAAEECQAHAA4BRAADAAEECQAIAA4BUgADAAEECQAJAA4BYE9yaWdpbmFsIGxpY2VuY2VOV0JKWkwrTmltYnVzUm9tTm85TC1NZWRpSXRhbFVua25vd251bmlxdWVJRE5XQkpaTCtOaW1idXNSb21ObzlMLU1lZGlJdGFsVmVyc2lvbiAwLjExVW5rbm93blVua25vd25Vbmtub3duAE8AcgBpAGcAaQBuAGEAbAAgAGwAaQBjAGUAbgBjAGUATgBXAEIASgBaAEwAKwBOAGkAbQBiAHUAcwBSAG8AbQBOAG8AOQBMAC0ATQBlAGQAaQBJAHQAYQBsAFUAbgBrAG4AbwB3AG4AdQBuAGkAcQB1AGUASQBEAE4AVwBCAEoAWgBMACsATgBpAG0AYgB1AHMAUgBvAG0ATgBvADkATAAtAE0AZQBkAGkASQB0AGEAbABWAGUAcgBzAGkAbwBuACAAMAAuADEAMQBVAG4AawBuAG8AdwBuAFUAbgBrAG4AbwB3AG4AVQBuAGsAbgBvAHcAbgADAAD/8LMzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA);
+ }
+ </style>
+</head>
+<body>
+ <canvas id="canvas" width="200" height="200"></canvas>
+ <script>
+ let canvas = document.getElementById('canvas');
+ canvas.mozPrintCallback = (obj) => {
+ let ctx = obj.context;
+ // Intentionally use a different fallback font than the test file so that
+ // if the font fails to load the test and reference will still be
+ // different.
+ ctx.font = '10px test, sans-serif';
+ ctx.fillText("lmnop", 20, 20);
+ obj.done();
+ window.postMessage("ready", "*");
+ };
+ </script>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/printpreview_helper.xhtml b/layout/base/tests/chrome/printpreview_helper.xhtml
new file mode 100644
index 0000000000..055f75c8f5
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_helper.xhtml
@@ -0,0 +1,1721 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window onload="runTests()"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <iframe style="min-height: 200px; min-width: 600px" type="content"></iframe>
+ <iframe style="min-height: 200px; min-width: 600px" type="content"></iframe>
+<script type="application/javascript">
+<![CDATA[
+// Note: We can't use window.frames directly here because the type="content"
+// attributes isolate the frames into their own BrowsingContext hierarchies.
+let frameElts = document.getElementsByTagName("iframe");
+
+var is = window.arguments[0].is;
+var isnot = window.arguments[0].isnot;
+var ok = window.arguments[0].ok;
+var todo = window.arguments[0].todo;
+var info = window.arguments[0].info;
+var SimpleTest = window.arguments[0].SimpleTest;
+var gWbp;
+var gPrintPreviewWindow;
+var gPrintPreviewBrowser;
+var ctx1;
+var ctx2;
+var counter = 0;
+
+var file = Cc["@mozilla.org/file/directory_service;1"]
+ .getService(Ci.nsIProperties)
+ .get("TmpD", Ci.nsIFile);
+filePath = file.path;
+
+function printpreview(options = {}) {
+ let resolve;
+ let promise = new Promise(r => { resolve = r });
+ var listener = {
+ onLocationChange: function(webProgress, request, location, flags) { },
+ onProgressChange: function(webProgress, request, curSelfProgress,
+ maxSelfProgress, curTotalProgress,
+ maxTotalProgress) {
+ info("onProgressChange", [...arguments].join(", "));
+ },
+ onSecurityChange: function(webProgress, request, state) { },
+ onStateChange: function(webProgress, request, stateFlags, status) {
+ info("onStateChange", [...arguments].join(", "));
+ if (stateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
+ setTimeout(resolve, 0);
+ }
+ },
+ onStatusChange: function(webProgress, request, status, message) {
+ info("onStatusChange", [...arguments].join(", "));
+ },
+ onContentBlockingEvent: function(webProgress, request, event) {
+ info("onContentBlockingEvent", [...arguments].join(", "));
+ },
+ QueryInterface: function(iid) {
+ if (iid.equals(Ci.nsIWebProgressListener) ||
+ iid.equals(Ci.nsISupportsWeakReference))
+ return this;
+ throw Components.Exception("", Cr.NS_NOINTERFACE);
+ }
+ }
+ var settings = Cc["@mozilla.org/gfx/printsettings-service;1"]
+ .getService(Ci.nsIPrintSettingsService).createNewPrintSettings();
+ settings.printBGColors = true;
+ settings.headerStrLeft = "";
+ settings.headerStrRight = "";
+ settings.footerStrLeft = "";
+ settings.footerStrRight = "";
+ settings.unwriteableMarginTop = 0;
+ settings.unwriteableMarginRight = 0;
+ settings.unwriteableMarginLeft = 0;
+ settings.unwriteableMarginBottom = 0;
+ if (options.settings) {
+ for (let key in options.settings) {
+ settings[key] = options.settings[key];
+ }
+ }
+ var before = 0;
+ var after = 0;
+ function beforeprint() { ++before; }
+ function afterprint() { ++after; }
+ frameElts[0].contentWindow.addEventListener("beforeprint", beforeprint, true);
+ frameElts[0].contentWindow.addEventListener("afterprint", afterprint, true);
+ {
+ let bc = frameElts[0].contentWindow.browsingContext;
+ let browser = document.createXULElement("browser");
+ browser.setAttribute("type", "content");
+ browser.style.minHeight = "800px";
+ browser.style.maxWidth = browser.style.minWidth = "800px";
+ browser.setAttribute("initialBrowsingContextGroupId", bc.group.id);
+ browser.setAttribute("nodefaultsrc", "true");
+ document.documentElement.appendChild(browser);
+ gPrintPreviewBrowser = browser;
+
+ // Force contentViewer creation and layout.
+ browser.browsingContext.docShell.document;
+ browser.getBoundingClientRect();
+
+ gPrintPreviewWindow = frameElts[0].contentWindow.printPreview(settings, listener, browser.browsingContext.docShell);
+ }
+ gWbp = gPrintPreviewWindow.docShell.contentViewer;
+ gWbp.QueryInterface(Ci.nsIWebBrowserPrint);
+ is(before, 1, "Should have called beforeprint listener!");
+ if (!options.hasMozPrintCallback) {
+ // If there's a mozPrintCallback the after print event won't fire until
+ // later.
+ is(after, 1, "Should have called afterprint listener!");
+ }
+ frameElts[0].contentWindow.removeEventListener("beforeprint", beforeprint, true);
+ frameElts[0].contentWindow.removeEventListener("afterprint", afterprint, true);
+ return promise;
+}
+
+function exitprintpreview() {
+ gPrintPreviewWindow.docShell.exitPrintPreview();
+ gPrintPreviewBrowser.remove();
+}
+
+function finish() {
+ SimpleTest.finish();
+ window.close();
+}
+
+async function runTests()
+{
+ // This ensures we actually test the lazy-load of images in printpreview_images.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.image-lazy-loading.root-margin.top", 0],
+ ["dom.image-lazy-loading.root-margin.bottom", 0],
+ ["dom.image-lazy-loading.root-margin.left", 0],
+ ["dom.image-lazy-loading.root-margin.right", 0],
+ ],
+ });
+ startTest1();
+}
+
+function compareCanvases(options = {}) {
+ const canvas1 = document.getElementsByTagName("canvas")[0];
+ const canvas2 = document.getElementsByTagName("canvas")[1];
+ let maxDifference = {};
+ const differingPixels = window.windowUtils.compareCanvases(canvas1, canvas2, maxDifference);
+ if (differingPixels) {
+ todo(false, "different: " + differingPixels + ", maxDifference: " + maxDifference.value);
+ todo(false, "TEST CASE: " + canvas1.toDataURL());
+ todo(false, "REFERENCE: " + canvas2.toDataURL());
+ }
+
+ let maxAllowedDifferent = options.maxDifferent || 0;
+ let maxAllowedDifference = options.maxDifference || 0;
+ return differingPixels <= maxAllowedDifferent && maxDifference.value <= maxAllowedDifference;
+}
+
+function addHTMLContent(parent) {
+ var n = parent.ownerDocument.createElement("div");
+ parent.appendChild(n);
+ var s = "<iframe width='500' height='40' src='data:text/plain,ThisIsAnIframeCreatedDuringPrintPreview'></iframe>";
+ s += "<table>";
+ for (var i = 1; i < 501; ++i) {
+ s += "<tr><td>Cell A" + i + "</td><td>Cell B" + i + "</td><td>Cell C" + i + "</td></tr>";
+ }
+ s += "</table>";
+ n.innerHTML = s;
+}
+
+async function startTest1() {
+ ctx1 = document.getElementsByTagName("canvas")[0].getContext("2d");
+ ctx2 = document.getElementsByTagName("canvas")[1].getContext("2d");
+ frameElts[0].contentDocument.body.innerHTML = "<div> </div><div>" + counter + " timers</div><div> </div>";
+
+ // Note this timeout is needed so that we can check that timers run
+ // after print preview, but not during it.
+ frameElts[0].contentWindow.wrappedJSObject.counter = counter;
+ frameElts[0].contentWindow.counterTimeout = "document.body.firstChild.nextSibling.innerHTML = ++counter + ' timers';" +
+ "window.setTimeout(counterTimeout, 0);";
+ frameElts[0].contentWindow.setTimeout(frameElts[0].contentWindow.counterTimeout, 0);
+ frameElts[0].contentDocument.body.firstChild.innerHTML = "Print preview";
+
+ await printpreview();
+ drawPrintPreviewWindow(ctx1);
+ frameElts[0].contentDocument.body.firstChild.innerHTML = "Galley presentation";
+
+ // Add some elements.
+ addHTMLContent(frameElts[0].contentDocument.body.lastChild);
+ // Delete them.
+ frameElts[0].contentDocument.body.lastChild.innerHTML = "";
+ // And readd.
+ addHTMLContent(frameElts[0].contentDocument.body.lastChild);
+
+ setTimeout(finalizeTest1, 1000);
+}
+
+function finalizeTest1() {
+ drawPrintPreviewWindow(ctx2);
+ exitprintpreview();
+ ok(compareCanvases(), "Canvas should be the same!");
+ counter = frameElts[0].contentWindow.counter;
+ // This timeout is needed so that we can check that timers do run after
+ // print preview.
+ setTimeout(runTest2, 1000);
+}
+
+function runTest2() {
+ isnot(frameElts[0].contentDocument.body.firstChild.nextSibling.textContent, "0 timers", "Timers should have run!");
+ isnot(frameElts[0].contentWindow.counter, 0, "Timers should have run!");
+ counter = frameElts[0].contentWindow.counter;
+ frameElts[0].contentWindow.counterTimeout = "";
+ setTimeout(runTest3, 0);
+}
+
+var elementIndex = 0;
+var compareEmptyElement = true;
+var emptyFormElements =
+ ["<input type='text'>",
+ "<input type='password'>",
+ "<input type='file'>",
+ "<input type='button'>",
+ "<input type='submit'>",
+ "<input type='reset'>",
+ "<input type='checkbox'>",
+ "<input type='radio'>",
+ "<select></select>",
+ "<select size='5'></select>",
+ "<textarea></textarea>"];
+
+var formElements =
+ ["<input type='text' value='text'>",
+ "<input type='password' value='password'>",
+ "<input type='file' value='" + filePath + "'>",
+ "<input type='button' value='button'>",
+ "<input type='submit' value='submit button'>",
+ "<input type='reset' value='reset button'>",
+ "<input type='checkbox' checked>",
+ "<input type='radio' checked>",
+ "<select><option>option1</option></select>",
+ "<select size='5'><option>1</option><option>2</option><option>3</option></select>",
+ "<textarea value='textarea'>textarea</textarea>"];
+
+function runTest3() {
+ if (compareEmptyElement) {
+ var currentIndex = elementIndex;
+ ++elementIndex;
+ if (elementIndex >= emptyFormElements.length) {
+ elementIndex = 0;
+ compareEmptyElement = false;
+ }
+ compareFormElementPrint(emptyFormElements[currentIndex], emptyFormElements[currentIndex], true);
+ return;
+ } else if (elementIndex < emptyFormElements.length) {
+ var currentIndex = elementIndex;
+ ++elementIndex;
+ compareFormElementPrint(emptyFormElements[currentIndex], formElements[currentIndex], false);
+ return;
+ }
+
+ setTimeout(runTest4, 0)
+}
+
+async function compareFormElementPrint(el1, el2, equals) {
+ frameElts[0].contentDocument.body.innerHTML = el1;
+ frameElts[0].contentDocument.body.firstChild.value =
+ frameElts[0].contentDocument.body.firstChild.getAttribute('value');
+ await printpreview();
+ drawPrintPreviewWindow(ctx1);
+ exitprintpreview();
+ frameElts[0].contentDocument.body.innerHTML = el2;
+ frameElts[0].contentDocument.body.firstChild.value =
+ frameElts[0].contentDocument.body.firstChild.getAttribute('value');
+ await printpreview();
+ drawPrintPreviewWindow(ctx2);
+ exitprintpreview();
+ is(compareCanvases(), equals,
+ "Comparing print preview didn't succeed [" + el1 + " : " + el2 + "]");
+ setTimeout(runTest3, 100);
+}
+
+// This is a crash test for bug 539060.
+function runTest4() {
+ frameElts[0].contentDocument.body.innerHTML =
+ "<iframe style='display: none;' src='data:text/html,<iframe>'></iframe>";
+ setTimeout(runTest4end, 500);
+}
+
+async function runTest4end() {
+ await printpreview();
+ exitprintpreview();
+
+ runTest5();
+}
+
+// This is a crash test for bug 595337
+async function runTest5() {
+ frameElts[0].contentDocument.body.innerHTML =
+ '<iframe style="position: fixed; visibility: hidden; bottom: 10em;"></iframe>' +
+ '<input contenteditable="true" style="display: table; page-break-before: left; width: 10000px;">';
+ await printpreview();
+ exitprintpreview();
+
+ setTimeout(runTest6, 0);
+}
+
+// Crash test for bug 878037
+function runTest6() {
+ frameElts[0].contentDocument.body.innerHTML =
+ '<style> li { list-style-image: url("animated.gif"); } </style>' +
+ '<li>Firefox will crash if you try and print this page</li>';
+
+ setTimeout(runTest6end, 500);
+}
+
+async function runTest6end() {
+ await printpreview();
+ exitprintpreview();
+
+ requestAnimationFrame(function() { setTimeout(runTest7); } );
+}
+
+async function runTest7() {
+ var contentText = "<a href='#'>mozilla</a><input>test<select><option>option1</option></select>";
+ // Create normal content
+ frameElts[0].contentDocument.body.innerHTML =
+ "<div>" + contentText + "</div>";
+ frameElts[0].contentDocument.body.firstChild.value =
+ frameElts[0].contentDocument.body.firstChild.getAttribute('value');
+ await printpreview();
+ drawPrintPreviewWindow(ctx1);
+ exitprintpreview();
+
+ frameElts[0].contentDocument.body.innerHTML = "<div></div>";
+ var sr = frameElts[0].contentDocument.body.firstChild.attachShadow({mode: "open"});
+ sr.innerHTML = contentText;
+ await printpreview();
+ drawPrintPreviewWindow(ctx2);
+ exitprintpreview();
+ ok(compareCanvases(), "Printing light DOM and shadow DOM should create same output");
+
+ requestAnimationFrame(function() { setTimeout(runTest8); } );
+}
+
+async function runTest8() {
+ // Test that fonts loaded with CSS and JS are printed the same.
+ const iframeElement = document.getElementsByTagName("iframe")[0];
+
+ // First, snapshot the page with font defined in CSS.
+ await new Promise((resolve) => {
+ iframeElement.addEventListener("load", resolve, { capture: true, once: true });
+ iframeElement.setAttribute("src", "printpreview_font_api_ref.html");
+ });
+ await printpreview();
+ drawPrintPreviewWindow(ctx1);
+ exitprintpreview();
+
+ // Second, snapshot the page with font loaded in JS.
+ await new Promise((resolve) => {
+ iframeElement.addEventListener("message", resolve, { capture: true, once: true });
+ iframeElement.setAttribute("src", "printpreview_font_api.html");
+ });
+ await printpreview();
+ drawPrintPreviewWindow(ctx2);
+ exitprintpreview();
+ ok(compareCanvases(), "Printing pages with fonts loaded from CSS and JS should be the same.");
+
+ requestAnimationFrame(function() { setTimeout(runTest9); } );
+}
+
+// Test for bug 1487649
+async function runTest9() {
+ frameElts[0].contentDocument.body.innerHTML = `
+ <svg width="100" height="100">
+ <rect width='100' height='100' fill='lime'/>
+ </svg>
+ `;
+
+ await printpreview();
+ drawPrintPreviewWindow(ctx1);
+ exitprintpreview();
+
+ frameElts[0].contentDocument.body.innerHTML = `
+ <svg width="100" height="100">
+ <defs>
+ <g id="useme">
+ <rect width='100' height='100' fill='lime'/>
+ </g>
+ </defs>
+ <use />
+ </svg>
+ `;
+
+ // Set the attribute explicitly because this is a chrome document, and the
+ // href attribute would get sanitized.
+ frameElts[0].contentDocument.querySelector("use").setAttribute("href", "#useme");
+
+ // Ensure the <use> shadow tree is created so we test what we want to test.
+ frameElts[0].contentDocument.body.offsetTop;
+
+ await printpreview();
+ drawPrintPreviewWindow(ctx2);
+ exitprintpreview();
+ ok(compareCanvases(), "Printing <use> subtrees should create same output");
+
+ requestAnimationFrame(function() { setTimeout(runTest10); } );
+}
+
+function drawPrintPreviewWindow(ctx) {
+ let width = gPrintPreviewWindow.innerWidth;
+ let height = gPrintPreviewWindow.innerHeight;
+ ctx.canvas.width = width;
+ ctx.canvas.height = height;
+ ctx.drawWindow(gPrintPreviewWindow, 0, 0, width, height, "rgb(255, 255, 255)");
+}
+
+// Test for bug 1524640
+async function runTest10() {
+ // Test that fonts loaded during mozprint callback are loaded into the cloned
+ // document.
+ const iframeElement = document.getElementsByTagName("iframe")[0];
+
+ // First, snapshot the page with font defined in CSS.
+ await new Promise((resolve) => {
+ iframeElement.addEventListener("load", resolve, { capture: true, once: true });
+ iframeElement.setAttribute("src", "printpreview_font_mozprintcallback_ref.html");
+ });
+ let mozPrintCallbackDone = new Promise((resolve) => {
+ iframeElement.addEventListener("message", resolve, { capture: true, once: true });
+ });
+ await printpreview({ hasMozPrintCallback: true });
+ await mozPrintCallbackDone;
+ drawPrintPreviewWindow(ctx1);
+ exitprintpreview();
+
+ // Second, snapshot the page with font loaded in JS.
+ await new Promise((resolve) => {
+ iframeElement.addEventListener("load", resolve, { capture: true, once: true });
+ iframeElement.setAttribute("src", "printpreview_font_mozprintcallback.html");
+ });
+ mozPrintCallbackDone = new Promise((resolve) => {
+ iframeElement.addEventListener("message", resolve, { capture: true, once: true });
+ });
+ await printpreview({ hasMozPrintCallback: true });
+ // Wait for the mozprintcallback to finish.
+ await mozPrintCallbackDone;
+ drawPrintPreviewWindow(ctx2);
+
+ exitprintpreview();
+ ok(compareCanvases(), "Printing pages with fonts loaded from a mozPrintCallback should be the same.");
+
+ requestAnimationFrame(function() { setTimeout(runTest11); } );
+}
+
+async function compareFiles(src1, src2, options = {}) {
+ const BASE = "https://example.org/chrome/layout/base/tests/chrome/";
+
+ info(`Comparing ${src1} with ${src2}`);
+ const iframeElement = document.getElementsByTagName("iframe")[0];
+
+ let messagePromise = null;
+ if (options.waitForMessage) {
+ messagePromise = new Promise(resolve => {
+ iframeElement.addEventListener("message", resolve, { capture: true, once: true });
+ });
+ }
+
+ await new Promise((resolve) => {
+ iframeElement.addEventListener("load", resolve, { capture: true, once: true });
+ iframeElement.setAttribute("src", new URL(src1, BASE).href);
+ });
+ let mediaElements = iframeElement.contentDocument.querySelectorAll(
+ "audio, video"
+ );
+ for (let mediaElement of mediaElements) {
+ let { widget } = SpecialPowers.wrap(iframeElement.contentWindow)
+ .windowGlobalChild.getActor("UAWidgets")
+ .widgets.get(mediaElement);
+ await widget.impl.Utils.l10n.translateRoots();
+ }
+
+ if (messagePromise) {
+ info("awaiting for message to arrive");
+ await messagePromise;
+ }
+
+ await printpreview(options.test || options);
+ drawPrintPreviewWindow(ctx1);
+ exitprintpreview();
+
+ await new Promise((resolve) => {
+ iframeElement.addEventListener("load", resolve, { capture: true, once: true });
+ iframeElement.setAttribute("src", new URL(src2, BASE).href);
+ });
+ mediaElements = iframeElement.contentDocument.querySelectorAll(
+ "audio, video"
+ );
+ for (let mediaElement of mediaElements) {
+ let { widget } = SpecialPowers.wrap(iframeElement.contentWindow)
+ .windowGlobalChild.getActor("UAWidgets")
+ .widgets.get(mediaElement);
+ await widget.impl.Utils.l10n.translateRoots();
+ }
+
+ await printpreview(options.ref || options);
+ drawPrintPreviewWindow(ctx2);
+ exitprintpreview();
+
+ is(compareCanvases(options), !options.expectDifference, `Printing ${src1} and ${src2} should${options.expectDifference ? ' not' : ''} produce the same results`);
+}
+
+// bug 1567105
+async function runTest11() {
+ await compareFiles("printpreview_quirks.html", "printpreview_quirks_ref.html");
+ requestAnimationFrame(function() { setTimeout(runTest12); } );
+}
+
+// bug 1621415
+async function runTest12() {
+ await compareFiles("test_document_adopted_styles.html", "test_document_adopted_styles_ref.html");
+ requestAnimationFrame(function() { setTimeout(runTest13); } );
+}
+
+// bug 1621415
+async function runTest13() {
+ await compareFiles("test_shadow_root_adopted_styles.html", "test_shadow_root_adopted_styles_ref.html");
+ requestAnimationFrame(function() { setTimeout(runTest14); } );
+}
+
+// bug 1622322
+async function runTest14() {
+ await compareFiles("test_shared_adopted_styles.html", "test_shared_adopted_styles_ref.html");
+ requestAnimationFrame(function() { setTimeout(runTest15); } );
+}
+
+// Crash test for bug 1615261
+async function runTest15() {
+ frameElts[0].contentDocument.body.innerHTML =
+ '<style>div { width: 100px; height: 100px; background-image: url("animated.gif"); } </style>' +
+ '<div>Firefox will crash if you try and print this page</div>';
+
+ // XXX Is there a more reliable way to wait for the background-image to load?
+ await new Promise(resolve => setTimeout(resolve, 500));
+
+ await printpreview();
+ await exitprintpreview();
+
+ requestAnimationFrame(function() { setTimeout(runTest16); } );
+}
+
+// Various image tests.
+async function runTest16() {
+ // fuzzy: SVG image in the test pixel-snaps different than <div> in the ref.
+ // (And on WebRender, the pixel-snapping seems to shift some pixels over a
+ // bit such that they're fully white vs. fully blue; hence 255 as the allowed
+ // color-channel difference.)
+ // XXXdholbert We should revisit this and adjust these thresholds (hopefully
+ // lower) after bug 1602410 lands.
+ await compareFiles("printpreview_images.html", "printpreview_images_ref.html", { maxDifferent: 118, maxDifference: 255 });
+ requestAnimationFrame(function() { setTimeout(runTest17); } );
+}
+
+async function runTest17() {
+ // fuzzy: SVG image in the test pixel-snaps different than <div> in the ref.
+ // (And on WebRender, the pixel-snapping seems to shift some pixels over a
+ // bit such that they're fully white vs. fully blue; hence 255 as the allowed
+ // color-channel difference.)
+ // XXXdholbert We should revisit this and adjust these thresholds (hopefully
+ // lower) after bug 1602410 lands.
+ await compareFiles("printpreview_images_sw.html", "printpreview_images_sw_ref.html", { waitForMessage: true, maxDifferent: 118, maxDifference: 255 });
+ requestAnimationFrame(() => setTimeout(runTest18));
+}
+
+async function runTest18() {
+ await compareFiles("printpreview_quirks.html", "printpreview_quirks_ref.html", {
+ settings: {
+ marginTop: 22,
+ marginBottom: 22,
+ marginLeft: 22,
+ marginRight: 22,
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest19));
+}
+
+async function runTest19() {
+ await compareFiles("color_adjust.html", "color_adjust_ref.html", {
+ test: {
+ settings: {
+ printBGColors: false,
+ printBGImages: false,
+ },
+ },
+ ref: {
+ settings: {
+ printBGColors: true,
+ printBGImages: true,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest20));
+}
+
+async function runTest20() {
+ frameElts[0].contentDocument.body.innerHTML =
+ '<style>div { page-break-after: always; }</style>' +
+ '<div>1</div>' +
+ '<div>2</div>' +
+ '<div>3</div>';
+ await printpreview();
+
+ is(gWbp.printPreviewCurrentPageNumber, 1,
+ "The initial current page number should be 1");
+
+ // Scroll to the second page.
+ gWbp.printPreviewScrollToPage(Ci.nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM, 2);
+
+ is(gWbp.printPreviewCurrentPageNumber, 2,
+ "The current page number should be 2");
+
+ // Scroll to the last page.
+ gWbp.printPreviewScrollToPage(Ci.nsIWebBrowserPrint.PRINTPREVIEW_END, 0);
+
+ is(gWbp.printPreviewCurrentPageNumber, 3,
+ "The current page number should be 3");
+
+ exitprintpreview();
+
+ requestAnimationFrame(() => setTimeout(runTest21));
+}
+
+async function runTest21() {
+ await compareFiles("data:text/html,<audio controls>", "data:text/html,<audio controls >"); // Shouldn't crash.
+ requestAnimationFrame(() => setTimeout(runTest22));
+}
+
+async function runTest22() {
+ // Similar to above runtTest20 but more specific for the logic to choose
+ // the current page in the new print preview UI so this test works only
+ // in the new print preview.
+ frameElts[0].contentDocument.body.innerHTML =
+ '<style>div { page-break-after: always; max-height: 2in; }</style>' +
+ '<div>1</div>' +
+ '<div>2</div>' +
+ '<div>3</div>' +
+ '<div>4</div>' +
+ '<div>5</div>' +
+ '<div>6</div>' +
+ '<div>7</div>' +
+ '<div>8</div>' +
+ '<div>9</div>' +
+ '<div>10</div>';
+
+ await printpreview({ settings: { paperHeight: 3 } });
+
+ const initialCurrentPageNumber = gWbp.printPreviewCurrentPageNumber;
+
+ // NOTE: In the cases the page hight is less than the half height of the
+ // print preview scroll port height, the initial current page number will
+ // not be 1.
+ ok(initialCurrentPageNumber >= 1,
+ "The initial current page number should be equal to or greater than 1");
+
+ const totalPageNumber = gWbp.printPreviewNumPages;
+ for (let n = initialCurrentPageNumber;
+ n <= totalPageNumber - initialCurrentPageNumber;
+ n++) {
+ // Scroll to the given page number and check the current page number.
+ gWbp.printPreviewScrollToPage(
+ Ci.nsIWebBrowserPrint.PRINTPREVIEW_GOTO_PAGENUM, n);
+ is(gWbp.printPreviewCurrentPageNumber, n,
+ `The current page number should be ${n}`);
+ }
+
+ // Scroll to the end of the scroll region.
+ gWbp.printPreviewScrollToPage(Ci.nsIWebBrowserPrint.PRINTPREVIEW_END, 0);
+
+ // Same as the initial current page number case, the last page might not
+ // be the current page if the page height is less than the half of the scroll
+ // port.
+ is(gWbp.printPreviewCurrentPageNumber,
+ totalPageNumber + 1 - initialCurrentPageNumber,
+ `The current page number should be ${totalPageNumber + 1 - initialCurrentPageNumber}`);
+
+ exitprintpreview();
+
+ requestAnimationFrame(() => setTimeout(runTest23));
+}
+
+async function runTest23() {
+ await compareFiles("printpreview_prettyprint.xml", "printpreview_prettyprint_ref.xhtml");
+ requestAnimationFrame(() => setTimeout(runTest24));
+}
+async function runTest24() {
+ await compareFiles("printpreview_mask.html", "data:text/html,", {
+ settings: {
+ printBGColors: false,
+ printBGImages: false,
+ },
+ expectDifference: true,
+ });
+ requestAnimationFrame(() => setTimeout(runTest25));
+}
+
+async function runTest25() {
+ await compareFiles("printpreview_downloadable_font.html", "printpreview_downloadable_font_ref.html");
+ requestAnimationFrame(() => setTimeout(runTest26));
+}
+
+async function runTest26() {
+ await compareFiles("printpreview_downloadable_font_in_iframe.html", "printpreview_downloadable_font_in_iframe_ref.html");
+ requestAnimationFrame(() => setTimeout(runTest27));
+}
+
+async function runTest27() {
+ await compareFiles("data:text/html,<style>:root { background-color: red; background-image: linear-gradient(red, red) }</style>", "data:text/html,", {
+ settings: {
+ printBGColors: false,
+ printBGImages: false,
+ }
+ });
+ requestAnimationFrame(() => setTimeout(runTest28));
+}
+
+async function runTest28() {
+ await compareFiles("data:text/html,<style>@page { margin: 0 }</style>Foo", "data:text/html,Foo", {
+ settings: {
+ honorPageRuleMargins: false,
+ marginTop: 1,
+ }
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest29));
+}
+
+async function runTest29() {
+ await compareFiles("data:text/html,<style>@page { margin: 0 }</style>Foo", "data:text/html,Foo", {
+ settings: {
+ honorPageRuleMargins: true,
+ marginTop: 1,
+ },
+ expectDifference: true,
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest30));
+}
+
+// Helper function to test trivial/unsupported pages-per-sheet values which we
+// just treat as 1 page-per-sheet (as if the attribute were unset.)
+// NOTE: The second data-URI's explicit "<body>" tag is not meant to affect the
+// rendering -- it's just a hack to ensure that the URIs themselves are
+// different, so that compareFiles() sees the two URIs as different and gets
+// the "load" notification that it depends on after switching between them.
+// We also include numPages in the tested URL/content here, so that if we get
+// a test-failure, it's easy to figure out which call to this function had the
+// failure.
+async function checkTrivialPagesPerSheetValue(numPages) {
+ let stringToDisplay = "TrivialPagesPerSheetVal" + numPages;
+ await compareFiles("data:text/html," + stringToDisplay,
+ "data:text/html,<body>" + stringToDisplay, {
+ test: {
+ settings: {
+ numPagesPerSheet: numPages,
+ },
+ },
+ ref: { settings: {} },
+ });
+}
+
+async function runTest30() {
+ await checkTrivialPagesPerSheetValue(1);
+ await checkTrivialPagesPerSheetValue(0);
+ await checkTrivialPagesPerSheetValue(-5);
+ await checkTrivialPagesPerSheetValue(7);
+ await checkTrivialPagesPerSheetValue(500);
+
+ requestAnimationFrame(() => setTimeout(runTest31));
+}
+
+// Helper function to test supported pages-per-sheet values that actually do
+// tiling (i.e. values greater than 1). We render the testcase and reference
+// case with zero page-margins and zero unwritable margins. (This makes it
+// tractable to create a reference case without having to account for margins
+// that are outside of the content area.)
+async function checkSupportedPagesPerSheetValue(src1, src2, numPages, fuzz) {
+ await compareFiles(src1, src2, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ numPagesPerSheet: numPages,
+ },
+ },
+ ref: {
+ settings: {
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+}
+
+// Pages-per-sheet: test the supported values.
+// First we test the perfect-square values: 4, 9, 16.
+// Then we test the other values, 2 and 6. (They require some extra bespoke
+// configuration to mock up their page-rotation, so their runTest functions are
+// a bit more verbose.)
+async function runTest31() {
+ // XXXdholbert On windows, our zero-margin settings aren't reliably respected
+ // for some reason; see bug 1680838. For now, we just account for that with a
+ // hefty amount of fuzz, guarded behind a platform-specific check so that we
+ // can keep this strict on other platforms.
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 101278, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ await checkSupportedPagesPerSheetValue("printpreview_pps4.html",
+ "printpreview_pps4_ref.html", 4, fuzz);
+
+ requestAnimationFrame(() => setTimeout(runTest32));
+}
+async function runTest32() {
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 130170, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ await checkSupportedPagesPerSheetValue("printpreview_pps9.html",
+ "printpreview_pps9_ref.html", 9, fuzz);
+
+ requestAnimationFrame(() => setTimeout(runTest33));
+}
+async function runTest33() {
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 145706, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ await checkSupportedPagesPerSheetValue("printpreview_pps16.html",
+ "printpreview_pps16_ref.html", 16,
+ fuzz);
+
+ requestAnimationFrame(() => setTimeout(runTest34));
+}
+
+async function runTest34() {
+ let fuzz = navigator.platform.includes("Win") ? // Workaround for bug 1680838
+ { maxDifferent: 44256, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "printpreview_pps2.html";
+ let ref = "printpreview_pps2_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ numPagesPerSheet: 2,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ orientation: 1, /* Landscape mode */
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest35));
+}
+
+async function runTest35() {
+ let fuzz = navigator.platform.includes("Win") ? // Workaround for bug 1680838
+ { maxDifferent: 88751, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "printpreview_pps6.html";
+ let ref = "printpreview_pps6_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 6,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ numPagesPerSheet: 6,
+ orientation: 1, /* Landscape mode */
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 6,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest36));
+}
+
+// Testcases for pages-per-sheet with nonzero unwriteable margin values:
+// ---------------------------------------------------------------------
+
+// In this subtest, the vertical scale-factor is more-severe and hence ends up
+// "winning", and we have a bit of extra space in the horizontal axis which we
+// distribute equally on either side (see the _ref.html file used below for
+// more details).
+async function runTest36() {
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 139464, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "printpreview_pps_uw4.html";
+ let ref = "printpreview_pps_uw4_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 4,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0.6,
+ unwriteableMarginRight: 0.1,
+ unwriteableMarginBottom: 0.4,
+ unwriteableMarginLeft: 0.3,
+ numPagesPerSheet: 4,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 4,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest37));
+}
+
+// In this subtest, the horizontal scale-factor is more-severe and hence ends
+// up "winning", and we have a bit of extra space in the vertical axis which we
+// distribute equally on either side (see the _ref.html file used below for
+// more details).
+async function runTest37() {
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 152268, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "printpreview_pps_uw9.html";
+ let ref = "printpreview_pps_uw9_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0.2,
+ unwriteableMarginRight: 0.8,
+ unwriteableMarginBottom: 0.4,
+ unwriteableMarginLeft: 1.2,
+ numPagesPerSheet: 9,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 10,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest38));
+}
+
+async function runTest38() {
+ let fuzz = navigator.platform.includes("Win") ? // Workaround for bug 1680838
+ { maxDifferent: 117744, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "printpreview_pps_uw2.html";
+ let ref = "printpreview_pps_uw2_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0.8,
+ unwriteableMarginRight: 0.6,
+ unwriteableMarginBottom: 1.2,
+ unwriteableMarginLeft: 0.4,
+ numPagesPerSheet: 2,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ /* Note: These are the same values we used for 'test' above, except
+ that here we've rotated the margins counterclockwise through the
+ sides, to account for the fact that we're specifying these margins
+ for a landscape-orientation page here vs. portrait-mode above.*/
+ unwriteableMarginTop: 0.6,
+ unwriteableMarginRight: 1.2,
+ unwriteableMarginBottom: 0.4,
+ unwriteableMarginLeft: 0.8,
+ orientation: 1, /* Landscape mode */
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest39));
+}
+
+// In this subtest, the vertical unwriteable margins exactly consume the full
+// pageHeight, so we don't have any space available for printing and we just
+// print a blank sheet. (This is mostly a stress test to be sure we don't
+// crash, hang, divide-by-zero, etc. in this edge case.)
+async function runTest39() {
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 254, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "data:text/html,Unwriteable-Margins-Too-Tall-To-See-This";
+ let ref = "data:text/html,<!-- runTest39 -->";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 4,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginTop: 3,
+ unwriteableMarginBottom: 2,
+ numPagesPerSheet: 4,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 4,
+ paperHeight: 5,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest40));
+}
+
+// In this subtest, the horizontal unwriteable margins consume more than the
+// full pageWidth, so we don't have any space available for printing and we
+// just print a blank sheet. (This is mostly a stress test to be sure we don't
+// crash, hang, divide-by-zero, etc. in this edge case.)
+async function runTest40() {
+ let fuzz = navigator.platform.includes("Win") ?
+ { maxDifferent: 172, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ let test = "data:text/html,Unwriteable-Margins-Too-Wide-To-See-This";
+ let ref = "data:text/html,<!-- runTest40 -->";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 4,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginRight: 3,
+ unwriteableMarginLeft: 4,
+ numPagesPerSheet: 9,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 4,
+ paperHeight: 5,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest41));
+}
+
+// Drawing headers/footers with very large unwriteable margins. The specific
+// bug occurs when combined unwriteable are larger than half of the page's
+// dimensions.
+async function runTest41() {
+ // This test compares a rendered page to the headers/footers that print code
+ // generates. On Windows, workaround bug 1680838. On OS X, the headers/
+ // footers are sometimes slightly offset. See bug 1714217.
+ // It's not too big a deal to have a higher fuzz factor, since when
+ // bug 1713404 occurs no headers/footers at all are rendered. These higher
+ // fuzz factors will still catch this worst case on OS X.
+ if (navigator.platform.includes("Win")) {
+ var fuzz = { maxDifferent: 133, maxDifference: 255 }; // Bug 1680838
+ }
+ else if (navigator.platform.includes("Mac")) {
+ var fuzz = { maxDifferent: 60, maxDifference: 200 }; // Bug 1714217
+ }
+ else {
+ var fuzz = { maxDifferent: 14, maxDifference: 16 };
+ }
+
+ let test = "data:text/html,<!-- runTest41 -->";
+ let ref = "printpreview_bug1713404_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 2,
+ unwriteableMarginRight: 2,
+ unwriteableMarginTop: 2,
+ unwriteableMarginBottom: 2,
+ headerStrLeft: "|",
+ headerStrRight: "||",
+ footerStrLeft: "|||",
+ footerStrRight: "||||",
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginBottom: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest42));
+}
+
+// Test that @page{ size: ... } works correctly with a smaller specified
+// @page{ size: ... } than the paper we are printing to. The test and ref use
+// varying margin and size properties to place elements in the same location.
+// This depends on Firefox's current behavior of putting undersized pages into
+// the upper left corner, rather than scaling or centering the page.
+async function runTest42() {
+ if (navigator.platform.includes("Mac")) {
+ var fuzz = { maxDifferent: 15, maxDifference: 8 };
+ }
+ let test = "print_page_size1.html";
+ let ref = "print_page_size1_ref.html";
+ await compareFiles(test, ref, fuzz);
+
+ requestAnimationFrame(() => setTimeout(runTest43));
+}
+
+// Test that @page{ size: ... } works correctly with a larger specified
+// @page{ size: ... } than the paper we are printing to. This specifically
+// tests scaling when only one page edge is too large.
+// This depends on Firefox's current behavior of scaling down any oversized
+// pages to fit onto a single physical page, and putting this aligned to the
+// upper left corner rather than centering the page.
+async function runTest43() {
+ if (navigator.platform.includes("Mac")) {
+ var fuzz = { maxDifferent: 15, maxDifference: 8 };
+ }
+ let test = "print_page_size2.html";
+ let ref = "print_page_size2_ref.html";
+ await compareFiles(test, ref, fuzz);
+
+ requestAnimationFrame(() => setTimeout(runTest44));
+}
+
+// Test that @page{ size: ... } works correctly with a smaller specified
+// @page{ size: ... } than the paper we are printing to. The test case uses
+// only the size property and the ref case uses only absolute positioning.
+// This depends on Firefox's current behavior of putting undersized pages into
+// the upper left corner, rather than scaling or centering the page.
+async function runTest44() {
+ if (navigator.platform.includes("Mac")) {
+ var fuzz = { maxDifferent: 15, maxDifference: 8 };
+ }
+ let test = "print_page_size3.html";
+ let ref = "print_page_size3_ref.html";
+ await compareFiles(test, ref, fuzz);
+ requestAnimationFrame(() => setTimeout(runTest45));
+}
+
+// Test that @page{ size: ... } results in scaling down the contents to fit on
+// a smaller paper size.
+// This depends on Firefox's current behavior of scaling oversized pages down
+// to fit onto the paper size.
+async function runTest45() {
+ if (navigator.platform.includes("Mac")) {
+ var fuzz = { maxDifferent: 15, maxDifference: 8 };
+ }
+ let test = "print_page_size4.html";
+ let ref = "print_page_size4_ref.html";
+ await compareFiles(test, ref, fuzz);
+ requestAnimationFrame(() => setTimeout(runTest46));
+}
+
+// Test that small elements don't get clipped from the bottom of the page when
+// using a < 1.0 scaling factor.
+async function runTest46() {
+ var fuzz = { maxDifferent: 0, maxDifference: 0 };
+ let test = "bug1722890.html";
+ let ref = "bug1722890_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ scaling: 0.5,
+ shrinkToFit: false
+ }
+ },
+ ref: {
+ settings: {
+ scaling: 1.0,
+ shrinkToFit: false
+ }
+ }
+ });
+ requestAnimationFrame(() => setTimeout(runTest47));
+}
+
+// Test for header/footer text clipping when printing with scaling factor.
+async function runTest47() {
+ // This test compares a rendered page to the headers/footers that print code
+ // generates. On Windows, workaround bug 1680838. On OS X, the headers/
+ // footers are sometimes slightly offset. See bug 1714217.
+ // It's not too big a deal to have a higher fuzz factor, since when
+ // bug 1730091 occurs most of the headers/footers are not rendered at all,
+ // and these fuzz factors will still catch that.
+ if (navigator.platform.includes("Win")) {
+ var fuzz = { maxDifferent: 200, maxDifference: 255 }; // Bug 1680838
+ }
+ else if (navigator.platform.includes("Mac")) {
+ var fuzz = { maxDifferent: 180, maxDifference: 255 }; // Bug 1714217
+ }
+ else {
+ var fuzz = { maxDifferent: 6, maxDifference: 16 };
+ }
+
+ let test = "data:text/html,<!-- runTest47 -->";
+ let ref = "printpreview_bug1730091_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ scaling: 1.3,
+ shrinkToFit: false,
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginBottom: 0,
+ headerStrLeft: "||||",
+ headerStrRight: "||||",
+ footerStrLeft: "||||",
+ footerStrRight: "||||",
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginBottom: 0,
+ },
+ },
+ });
+ requestAnimationFrame(() => setTimeout(runTest48));
+}
+
+// Test that even when downscaling happens due to CSS page-size, the
+// unwriteable margins are in units applicable to the resulting page as it is
+// actually printed.
+// https://bugzilla.mozilla.org/1769161
+async function runTest48() {
+ if (navigator.platform.includes("Win")) {
+ var fuzz = { maxDifferent: 816, maxDifference: 255 }; // Bug 1680838
+ } else {
+ var fuzz = { maxDifferent: 16, maxDifference: 255 };
+ }
+ let test = "bug1769161_1.html";
+ let ref = "bug1769161_1_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginTop: 1,
+ unwriteableMarginBottom: 1,
+ },
+ });
+ requestAnimationFrame(() => setTimeout(runTest49));
+}
+
+// Same as runTest48, but uses different scaling factors.
+async function runTest49() {
+ if (navigator.platform.includes("Win")) {
+ var fuzz = { maxDifferent: 6472, maxDifference: 255 }; // Bug 1680838
+ } else {
+ var fuzz = { maxDifferent: 24, maxDifference: 255 };
+ }
+ let test = "bug1769161_2.html";
+ let ref = "bug1769161_2_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginTop: 1,
+ unwriteableMarginBottom: 1,
+ },
+ });
+ requestAnimationFrame(() => setTimeout(runTest50));
+}
+
+// Test that when downscaling happens due to CSS page-size, the unwriteable
+// margins are equivalent to the @page margins after those margins are scaled.
+// https://bugzilla.mozilla.org/1769161
+async function runTest50() {
+ if (navigator.platform.includes("Win")) {
+ var fuzz = { maxDifferent: 816, maxDifference: 255 }; // Bug 1680838
+ } else {
+ var fuzz = { maxDifferent: 16, maxDifference: 255 };
+ }
+ let test = "bug1769161_3.html";
+ let ref = "bug1769161_3_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginTop: 1,
+ unwriteableMarginBottom: 1,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginBottom: 0,
+ },
+ },
+ });
+ requestAnimationFrame(() => setTimeout(runTest51));
+}
+
+// Same as runTest50, but uses different scaling factors.
+async function runTest51() {
+ if (navigator.platform.includes("Win")) {
+ var fuzz = { maxDifferent: 11764, maxDifference: 255 }; // Bug 1680838
+ } else {
+ var fuzz = { maxDifferent: 24, maxDifference: 255 };
+ }
+ let test = "bug1769161_4.html";
+ let ref = "bug1769161_4_ref.html";
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginTop: 1,
+ unwriteableMarginBottom: 1,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 5,
+ paperHeight: 5,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ unwriteableMarginLeft: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginBottom: 0,
+ },
+ },
+ });
+ requestAnimationFrame(() => setTimeout(runTest52));
+}
+
+async function runTest52() {
+ // Unwriteable margins can be ignored by setting the appropriate flag.
+ // Slightly different to avoid hang.
+ await compareFiles("data:text/html,Foo", "data:text/html,<div>Foo", {
+ maxDifferent: 0,
+ maxDifference: 0,
+ test: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginBottom: 1,
+ unwriteableMarginLeft: 1,
+ ignoreUnwriteableMargins: true,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest53));
+}
+
+async function runTest53() {
+ // Ensure that even when unwriteable margins are set to be taken into
+ // account, page rule margins can override it.
+ await compareFiles(
+ "data:text/html,<style>@page { margin: 0 }</style>Foo",
+ "data:text/html,<div>Foo",
+ {
+ maxDifferent: 0,
+ maxDifference: 0,
+ test: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginBottom: 1,
+ unwriteableMarginLeft: 1,
+ ignoreUnwriteableMargins: false,
+ honorPageRuleMargins: true,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest54));
+}
+
+async function runTest54() {
+ // `ignoreUnwriteableMargins` lets author-specified margins ignore
+ // unwriteable margins as well. Without this flag, the unwriteable
+ // margin is ignored iff `Margins: Default` is set (i.e.
+ // `honorPageRuleMargins` set) and the author specified CSS page
+ // margin is zero.
+ // Note: At least currently, both `ignoreUnwriteableMargins`
+ // and `honorPageRuleMargins` cannot be set through the printing UI.
+ // TODO: If this behaviour is desired is up for debate.
+ await compareFiles(
+ "data:text/html,<style>@page { margin: 0.1in }</style>Foo",
+ "data:text/html,<div>Foo",
+ {
+ maxDifferent: 0,
+ maxDifference: 0,
+ test: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 1,
+ unwriteableMarginRight: 1,
+ unwriteableMarginBottom: 1,
+ unwriteableMarginLeft: 1,
+ ignoreUnwriteableMargins: true,
+ honorPageRuleMargins: true,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0.1,
+ marginRight: 0.1,
+ marginBottom: 0.1,
+ marginLeft: 0.1,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest55));
+}
+
+async function runTest55() {
+ let test = "printpreview_pps_uw2.html";
+ let ref = "printpreview_pps_uw2_no_margin_ref.html";
+ let fuzz = navigator.platform.includes("Win") ? // Workaround for bug 1680838
+ { maxDifferent: 12870, maxDifference: 255 } :
+ { maxDifferent: 0, maxDifference: 0 };
+
+ // Unwriteable margins are successfully ignored, if requested,
+ // for pages-per-sheet.
+ await compareFiles(test, ref, {
+ maxDifferent: fuzz.maxDifferent,
+ maxDifference: fuzz.maxDifference,
+ test: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ paperSizeUnit: Ci.nsIPrintSettings.kPaperSizeInches,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0.8,
+ unwriteableMarginRight: 0.6,
+ unwriteableMarginBottom: 1.2,
+ unwriteableMarginLeft: 0.4,
+ numPagesPerSheet: 2,
+ ignoreUnwriteableMargins: true,
+ },
+ },
+ ref: {
+ settings: {
+ paperWidth: 8,
+ paperHeight: 10,
+ marginTop: 0,
+ marginRight: 0,
+ marginBottom: 0,
+ marginLeft: 0,
+ unwriteableMarginTop: 0,
+ unwriteableMarginRight: 0,
+ unwriteableMarginBottom: 0,
+ unwriteableMarginLeft: 0,
+ orientation: 1, /* Landscape mode */
+ },
+ },
+ });
+
+ requestAnimationFrame(() => setTimeout(runTest56));
+}
+
+async function runTest56() {
+ await compareFiles("printpreview_image_select.html", "printpreview_image_select_ref.html");
+ requestAnimationFrame(() => setTimeout(runTest57));
+}
+
+// Tests that printing with mixed page sizes doesn't crash.
+// These tests can't actually compare any pages after the first, so this only
+// verifies that we don't crash reflowing.
+async function runTest57() {
+ let test = "printpreview_mixed_page_size_001.html";
+ // The params are just to give the file unique URLs.
+ await compareFiles(test + "?test", test + "?ref");
+ requestAnimationFrame(() => setTimeout(runTest58));
+}
+
+// As with runTest57, this is only testing for crashes.
+// This includes fixed-position content, as this is the only way to get content
+// within the same chain of continuations onto pages with different sizes.
+async function runTest58() {
+ let test = "printpreview_mixed_page_size_002.html";
+ // The params are just to give the file unique URLs.
+ await compareFiles(test + "?test", test + "?ref");
+ finish();
+}
+
+]]></script>
+<table style="border: 1px solid black;" xmlns="http://www.w3.org/1999/xhtml">
+<tr><th>Print preview canvas 1</th><th>Print preview canvas 2</th></tr>
+<tr>
+<td><canvas height="800" width="800"></canvas></td>
+<td><canvas height="800" width="800"></canvas></td>
+</tr></table>
+</window>
diff --git a/layout/base/tests/chrome/printpreview_image_select.html b/layout/base/tests/chrome/printpreview_image_select.html
new file mode 100644
index 0000000000..b61a40774d
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_image_select.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<body>
+ <picture>
+ <source width="200" srcset="red.png 1w, green.png 200w">
+ <img>
+ </picture>
+</body>
diff --git a/layout/base/tests/chrome/printpreview_image_select_ref.html b/layout/base/tests/chrome/printpreview_image_select_ref.html
new file mode 100644
index 0000000000..7189a57642
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_image_select_ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body>
+ <div style="width: 200px; height: 200px; background-color: green;"></div>
+</body>
diff --git a/layout/base/tests/chrome/printpreview_images.html b/layout/base/tests/chrome/printpreview_images.html
new file mode 100644
index 0000000000..6919002354
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_images.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<style>
+ img, object, svg, input { display: block }
+ div {
+ content: url(blue-32x32.png);
+ width: 32px;
+ height: 32px;
+ }
+</style>
+<div></div>
+<picture>
+ <source srcset="blue-32x32.png">
+ <img width=32 height=32>
+</picture>
+<picture>
+ <source srcset="blue-32x32.png" media="print">
+ <source srcset="animated.gif" media="not print">
+ <img width=32 height=32>
+</picture>
+<img src="blue-32x32.png" width=32 height=32>
+<object data="blue-32x32.png" width=32 height=32></object>
+<svg width="32" height="32">
+ <image x=0 y=0 href="blue-32x32.png" width=32 height=32></image>
+</svg>
+<input type="image" src="blue-32x32.png" width=32 height=32>
diff --git a/layout/base/tests/chrome/printpreview_images_ref.html b/layout/base/tests/chrome/printpreview_images_ref.html
new file mode 100644
index 0000000000..65a0df066c
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_images_ref.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<style>
+ div {
+ width: 32px;
+ height: 32px;
+ background-color: blue;
+ }
+</style>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
diff --git a/layout/base/tests/chrome/printpreview_images_sw.html b/layout/base/tests/chrome/printpreview_images_sw.html
new file mode 100644
index 0000000000..78b8a0dd88
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_images_sw.html
@@ -0,0 +1,46 @@
+<!doctype html>
+<style>
+ img, object, svg, input { display: block }
+ div {
+ content: url(nonexistent.png?1);
+ width: 32px;
+ height: 32px;
+ }
+</style>
+<script>
+const WORKER = "printpreview_images_sw.js";
+if (location.href.includes("registered")) {
+ console.log("REGISTERED");
+ onload = function() {
+ postMessage("ready", "*");
+ }
+ onbeforeunload = function() {
+ navigator.serviceWorker.getRegistrations().then(function(registrations) {
+ for(let registration of registrations) {
+ registration.unregister()
+ }
+ })
+ navigator.serviceWorker.unregister(WORKER);
+ }
+} else {
+ navigator.serviceWorker.oncontrollerchange = function() {
+ location.href = location.href + "?registered";
+ };
+ navigator.serviceWorker.register(WORKER);
+}
+</script>
+<div></div>
+<picture>
+ <source srcset="nonexistent.png?2">
+ <img width=32 height=32>
+</picture>
+<picture>
+ <source srcset="nonexistent.png?3" media="print">
+ <source srcset="animated.gif" media="not print">
+ <img width=32 height=32>
+</picture>
+<img src="nonexistent.png?4" width=32 height=32>
+<svg width="32" height="32">
+ <image x=0 y=0 href="nonexistent.png?7" width=32 height=32></image>
+</svg>
+<input type="image" src="nonexistent.png?6" width=32 height=32>
diff --git a/layout/base/tests/chrome/printpreview_images_sw.js b/layout/base/tests/chrome/printpreview_images_sw.js
new file mode 100644
index 0000000000..bb0ab60b1f
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_images_sw.js
@@ -0,0 +1,11 @@
+self.addEventListener("fetch", event => {
+ if (event.request.url.includes("nonexistent.png")) {
+ event.respondWith(
+ fetch(event.request.url.replace("nonexistent.png", "blue-32x32.png"))
+ );
+ }
+});
+
+self.addEventListener("activate", event => {
+ event.waitUntil(clients.claim());
+});
diff --git a/layout/base/tests/chrome/printpreview_images_sw_ref.html b/layout/base/tests/chrome/printpreview_images_sw_ref.html
new file mode 100644
index 0000000000..2efb9e9199
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_images_sw_ref.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<style>
+ div {
+ width: 32px;
+ height: 32px;
+ background-color: blue;
+ }
+</style>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
+<div></div>
diff --git a/layout/base/tests/chrome/printpreview_mask.html b/layout/base/tests/chrome/printpreview_mask.html
new file mode 100644
index 0000000000..f1ea3af255
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_mask.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<style>
+div {
+ background: black;
+ color: white;
+ -webkit-mask-image: linear-gradient(180deg,#000 60%,transparent);
+ mask-image: linear-gradient(180deg,#000 60%,transparent);
+}
+</style>
+<div>
+ Here's some text<br>
+ Here's some text<br>
+ Here's some text<br>
+ Here's some text<br>
+ Here's some text<br>
+</div>
diff --git a/layout/base/tests/chrome/printpreview_mixed_page_size_001.html b/layout/base/tests/chrome/printpreview_mixed_page_size_001.html
new file mode 100644
index 0000000000..a611299527
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_mixed_page_size_001.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<style>
+@page a {
+ size: 10in 5in;
+}
+@page b {
+ size: 6in 9in;
+}
+</style>
+<div style="page: a">a</div>
+<div style="page: b">b</div>
diff --git a/layout/base/tests/chrome/printpreview_mixed_page_size_002.html b/layout/base/tests/chrome/printpreview_mixed_page_size_002.html
new file mode 100644
index 0000000000..f55efef3e6
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_mixed_page_size_002.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<style>
+@page a {
+ size: 10in 5in;
+}
+@page b {
+ size: 6in 9in;
+}
+</style>
+<div style="display: flex; position: fixed;">
+ <div>static 1</div>
+ <div>static 2</div>
+</div>
+<div style="page: a">block a</div>
+<div style="page: b">block b</div>
diff --git a/layout/base/tests/chrome/printpreview_pps16.html b/layout/base/tests/chrome/printpreview_pps16.html
new file mode 100644
index 0000000000..fc94819340
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps16.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "16-pages-per-sheet" scenario.
+ There are 16 full-page color-swatches. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 120px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+.swatch:nth-child(5) { border-color: purple; }
+.swatch:nth-child(6) { border-color: olive; }
+.swatch:nth-child(7) { border-color: blue; }
+.swatch:nth-child(8) { border-color: tan; }
+.swatch:nth-child(9) { border-color: fuchsia; }
+.swatch:nth-child(10) { border-color: salmon; }
+.swatch:nth-child(11) { border-color: lightgreen; }
+.swatch:nth-child(12) { border-color: navy; }
+.swatch:nth-child(13) { border-color: brown; }
+.swatch:nth-child(14) { border-color: orchid; }
+.swatch:nth-child(15) { border-color: goldenrod; }
+.swatch:nth-child(16) { border-color: seagreen; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps16_ref.html b/layout/base/tests/chrome/printpreview_pps16_ref.html
new file mode 100644
index 0000000000..5807cc44ec
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps16_ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "16-pages-per-sheet" scenario. The width
+ and height of each "swatch" is 1/4 the width and height of the page. -->
+<style>
+html, body { margin: 0; height: 100%; }
+body {
+ display: flex;
+ flex-flow: row wrap;
+}
+.swatch {
+ box-sizing: border-box;
+ border: 30px solid;
+ /* The height will be automatically set to the flex container's row height,
+ via default 'align-self' behavior (which ends up as 'stretch') */
+ flex: 1 25%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+.swatch:nth-child(5) { border-color: purple; }
+.swatch:nth-child(6) { border-color: olive; }
+.swatch:nth-child(7) { border-color: blue; }
+.swatch:nth-child(8) { border-color: tan; }
+.swatch:nth-child(9) { border-color: fuchsia; }
+.swatch:nth-child(10) { border-color: salmon; }
+.swatch:nth-child(11) { border-color: lightgreen; }
+.swatch:nth-child(12) { border-color: navy; }
+.swatch:nth-child(13) { border-color: brown; }
+.swatch:nth-child(14) { border-color: orchid; }
+.swatch:nth-child(15) { border-color: goldenrod; }
+.swatch:nth-child(16) { border-color: seagreen; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps2.html b/layout/base/tests/chrome/printpreview_pps2.html
new file mode 100644
index 0000000000..a7f9fddae3
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "2-pages-per-sheet" scenario.
+ There are 2 full-page "swatches" with large colorful borders. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 240px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: pink; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps2_ref.html b/layout/base/tests/chrome/printpreview_pps2_ref.html
new file mode 100644
index 0000000000..b1e8033c87
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps2_ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "2-pages-per-sheet" scenario. The width and
+ height of each "swatch" is 0.625 the width and height of the page. That
+ 0.625 scale-factor comes from the fact that, when rendering the testcase,
+ we'll need to scale the page-width (8in) down enough to fit side-by-side
+ into half of the page height (10in/2 = 5in), on a rotated sheet. So we've
+ got to scale 8in to fit into 5in, which is a scale factor of 0.625. -->
+<style>
+html { height: 100%; }
+body {
+ height: 100%;
+ margin: 0;
+ box-sizing: border-box;
+
+ /* The testcase (rendered at 2-pages-per-sheet) will have 1.75in of extra
+ space in the vertical axis, which will be distributed equally with 0.875in
+ on the top and the bottom of the page grid. We mock that up as padding
+ here: */
+ padding: 0.875in 0;
+
+ /* We lay out the body as a row-oriented flex container, with two
+ side-by-side children which correspond to the two pages per sheet: */
+ display: flex;
+}
+.swatch {
+ box-sizing: border-box;
+
+ /* This represents the 240px border in the testcase, scaled down 0.625x: */
+ border: 150px solid;
+
+ /* Share the width equally among the swatches. (The height will be
+ automatically set to the flex container's row height, via default
+ 'align-self' behavior.) */
+ flex: 1;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: pink; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps4.html b/layout/base/tests/chrome/printpreview_pps4.html
new file mode 100644
index 0000000000..6e3d030c6d
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps4.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "4-pages-per-sheet" scenario.
+ There are 4 full-page "swatches" with large colorful borders. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 120px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps4_ref.html b/layout/base/tests/chrome/printpreview_pps4_ref.html
new file mode 100644
index 0000000000..4ffbec505f
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps4_ref.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "4-pages-per-sheet" scenario. The width
+ and height of each "swatch" is 1/2 the width and height of the page. -->
+<style>
+html, body { margin: 0; height: 100%; }
+body {
+ display: flex;
+ flex-flow: row wrap;
+}
+.swatch {
+ box-sizing: border-box;
+ border: 60px solid;
+ /* The height will be automatically set to the flex container's row height,
+ via default 'align-self' behavior (which ends up as 'stretch') */
+ flex: 1 50%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps6.html b/layout/base/tests/chrome/printpreview_pps6.html
new file mode 100644
index 0000000000..68722afba9
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps6.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "6-pages-per-sheet" scenario.
+ There are 12 full-page color-swatches, which should be layed out over 2
+ sheets.
+
+ This test is specifically given more pages than fit on a sheet so that
+ at least one of our tests checks the case where we have more than one
+ sheet. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 120px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+.swatch:nth-child(5) { border-color: purple; }
+.swatch:nth-child(6) { border-color: olive; }
+.swatch:nth-child(7) { border-color: blue; }
+.swatch:nth-child(8) { border-color: tan; }
+.swatch:nth-child(9) { border-color: fuchsia; }
+.swatch:nth-child(10) { border-color: salmon; }
+.swatch:nth-child(11) { border-color: lightgreen; }
+.swatch:nth-child(12) { border-color: navy; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps6_ref.html b/layout/base/tests/chrome/printpreview_pps6_ref.html
new file mode 100644
index 0000000000..3af174e0fa
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps6_ref.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "6-pages-per-sheet" scenario. This file is
+ a mockup of a sheet with 6 pages, followed by a sheet with another 6 pages,
+ with the pages all having a 0.4x scale factor applied. -->
+<style>
+html { height: 100%; }
+body {
+ height: 100%;
+ margin: 0;
+ box-sizing: border-box;
+}
+
+.sheet {
+ height: 100%;
+
+ /* We lay out the body as a column-oriented flex container (whose children,
+ in turn, are rows). */
+ display: flex;
+ flex-direction: column;
+}
+
+.row {
+ /* Give each row an equal share of the available height: */
+ flex: 1;
+
+ /* ...and render them as row-oriented (by default) flex containers: */
+ display: flex;
+}
+
+.swatch {
+ box-sizing: border-box;
+
+ /* This represents the 120px border in the testcase, scaled down 0.4x: */
+ border: 48px solid;
+
+ /* Share the width equally among the swatches. (The height will be
+ automatically set to the flex container's row height, via default
+ 'align-self' behavior.) */
+ flex: 1;
+
+ /* The testcase (rendered at 6-pages-per-sheet) will have 0.2in of extra
+ space in the horizontal axis, which will be distributed equally with
+ 0.05in to the left and right of each page in the grid. We mock that up as
+ margin here: */
+ margin: 0 0.05in;
+}
+
+.sheet:nth-child(1) > .row:nth-child(1) > .swatch:nth-child(1) { border-color: cyan; }
+.sheet:nth-child(1) > .row:nth-child(1) > .swatch:nth-child(2) { border-color: yellow; }
+.sheet:nth-child(1) > .row:nth-child(2) > .swatch:nth-child(1) { border-color: pink; }
+.sheet:nth-child(1) > .row:nth-child(2) > .swatch:nth-child(2) { border-color: orange; }
+.sheet:nth-child(1) > .row:nth-child(3) > .swatch:nth-child(1) { border-color: purple; }
+.sheet:nth-child(1) > .row:nth-child(3) > .swatch:nth-child(2) { border-color: olive; }
+
+.sheet:nth-child(2) { break-before: always; }
+.sheet:nth-child(2) > .row:nth-child(1) > .swatch:nth-child(1) { border-color: blue; }
+.sheet:nth-child(2) > .row:nth-child(1) > .swatch:nth-child(2) { border-color: tan; }
+.sheet:nth-child(2) > .row:nth-child(2) > .swatch:nth-child(1) { border-color: fuchsia; }
+.sheet:nth-child(2) > .row:nth-child(2) > .swatch:nth-child(2) { border-color: salmon; }
+.sheet:nth-child(2) > .row:nth-child(3) > .swatch:nth-child(1) { border-color: lightgreen; }
+.sheet:nth-child(2) > .row:nth-child(3) > .swatch:nth-child(2) { border-color: navy; }
+</style>
+<div class="sheet">
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+</div>
+<div class="sheet">
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+</div>
diff --git a/layout/base/tests/chrome/printpreview_pps9.html b/layout/base/tests/chrome/printpreview_pps9.html
new file mode 100644
index 0000000000..341e5fdf81
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps9.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "9-pages-per-sheet" scenario.
+ There are 9 full-page color-swatches. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 120px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+.swatch:nth-child(5) { border-color: purple; }
+.swatch:nth-child(6) { border-color: olive; }
+.swatch:nth-child(7) { border-color: blue; }
+.swatch:nth-child(8) { border-color: tan; }
+.swatch:nth-child(9) { border-color: fuchsia; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps9_ref.html b/layout/base/tests/chrome/printpreview_pps9_ref.html
new file mode 100644
index 0000000000..ab90c837aa
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps9_ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "9-pages-per-sheet" scenario. The width
+ and height of each "swatch" is 1/3 the width and height of the page. -->
+<style>
+html, body { margin: 0; height: 100%; }
+body {
+ display: flex;
+ flex-flow: row wrap;
+}
+.swatch {
+ box-sizing: border-box;
+ border: 40px solid;
+ /* The height will be automatically set to the flex container's row height,
+ via default 'align-self' behavior (which ends up as 'stretch') */
+ /* Note: it's OK that the flex-basis isn't exactly 1/3 here.
+ Flexbox layout will give each flex item 33% of the width,
+ and then divide up the remaining amount equally. This
+ results in exactly 3 items fitting per row and each one
+ getting 1/3 of a row, which is all we're going for. */
+ flex: 1 33%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+.swatch:nth-child(5) { border-color: purple; }
+.swatch:nth-child(6) { border-color: olive; }
+.swatch:nth-child(7) { border-color: blue; }
+.swatch:nth-child(8) { border-color: tan; }
+.swatch:nth-child(9) { border-color: fuchsia; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw2.html b/layout/base/tests/chrome/printpreview_pps_uw2.html
new file mode 100644
index 0000000000..34d3af49de
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw2.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "2-pages-per-sheet" scenario with nonzero
+ unwriteable margins. There are 2 full-page "swatches" with large colorful
+ borders. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 240px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: pink; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw2_no_margin_ref.html b/layout/base/tests/chrome/printpreview_pps_uw2_no_margin_ref.html
new file mode 100644
index 0000000000..223aab8d55
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw2_no_margin_ref.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "2-pages-per-sheet" scenario zero
+ unwriteable margins. We're scaling down 8in width to fit 2 in 10in,
+ so the scale factor is (10 / 2) / 8 = 0.625x. -->
+<style>
+html { height: 100%; }
+body {
+ height: 100%;
+ margin: 0;
+ box-sizing: border-box;
+
+ /* The testcase (rendered at 2-pages-per-sheet) will have 8 - (10in * 0.625)
+ * = 1.75in of extra vertical space. */
+ padding: 0.875in 0;
+
+ /* We lay out the body as a row-oriented flex container, with two
+ side-by-side children which correspond to the two pages per sheet: */
+ display: flex;
+}
+.swatch {
+ box-sizing: border-box;
+
+ /* This represents the 240px border in the testcase, scaled down 0.5x: */
+ border: 150px solid;
+
+ /* Share the width equally among the swatches. (The height will be
+ automatically set to the flex container's row height, via default
+ 'align-self' behavior.) */
+ flex: 1;
+ margin: 0;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: pink; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw2_ref.html b/layout/base/tests/chrome/printpreview_pps_uw2_ref.html
new file mode 100644
index 0000000000..d923901096
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw2_ref.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "2-pages-per-sheet" scenario with nonzero
+ unwriteable margins. We expect the pages to have a 0.5x scale-down
+ factor. That 0.5 scale-factor comes from the fact that, when rendering the
+ testcase, we'll need to scale the page-width (8in) down enough to fit
+ side-by-side into half of the sheet's available width (with the sheet
+ having been rotated to landscape mode), with the sheet's unwritable margin
+ having already been subtracted out. The sheet's width (in landscape mode)
+ is 10in, and its unwriteable margin in that axis is 2in, so it's got 8in
+ of available width to hold two side-by-side pages, i.e. 4in per page.
+ Since the page width was 8in, that makes for a 0.5x scale. -->
+<style>
+html { height: 100%; }
+body {
+ height: 100%;
+ margin: 0;
+ box-sizing: border-box;
+
+ /* The testcase (rendered at 2-pages-per-sheet) will have 2in of extra space
+ in the vertical axis, which will be distributed equally with 1in on the
+ top and the bottom of the page grid (separately from the sheet's
+ unwriteable margin). We mock that up as padding here: */
+ padding: 1in 0;
+
+ /* We lay out the body as a row-oriented flex container, with two
+ side-by-side children which correspond to the two pages per sheet: */
+ display: flex;
+}
+.swatch {
+ box-sizing: border-box;
+
+ /* This represents the 240px border in the testcase, scaled down 0.5x: */
+ border: 120px solid;
+
+ /* Share the width equally among the swatches. (The height will be
+ automatically set to the flex container's row height, via default
+ 'align-self' behavior.) */
+ flex: 1;
+
+ /* This margin is meant to mock up the unwriteable margin for each page on
+ our sheet; it's exactly 0.5x the unwriteableMargin values specified for
+ the testcase in printpreview_helper.xhtml. */
+ margin: 0.4in 0.3in 0.6in 0.2in;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: pink; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw4.html b/layout/base/tests/chrome/printpreview_pps_uw4.html
new file mode 100644
index 0000000000..6e3d030c6d
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw4.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "4-pages-per-sheet" scenario.
+ There are 4 full-page "swatches" with large colorful borders. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 120px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw4_ref.html b/layout/base/tests/chrome/printpreview_pps_uw4_ref.html
new file mode 100644
index 0000000000..a603ff2be9
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw4_ref.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "4-pages-per-sheet" scenario. This file is
+ a mockup of a sheet with 4 pages, with the pages all having a 0.4x scale
+ scale factor applied. (We end up with that scale factor by subtracting the
+ requested unwriteable margins from the sheet, and dividing up the
+ remaining space equally among the "virtual pages".) -->
+<style>
+html {
+ display: flex;
+ height: 100%;
+ margin: 0;
+}
+body {
+ /* As a flex item (a child of the html element), fill the available area. */
+ flex: 1;
+
+ /* We lay out the body as a column-oriented flex container (whose children,
+ in turn, are rows). */
+ display: flex;
+ flex-direction: column;
+
+ /* These values come directly from the unwriteableMargin values in the
+ testcase's configuration code in printpreview_helper.xhtml. */
+ margin-top: 0.6in;
+ margin-right: 0.1in;
+ margin-bottom: 0.4in;
+ margin-left: 0.3in;
+}
+
+.row {
+ /* Give each row an equal share of the available height: */
+ flex: 1;
+
+ /* ...and render them as row-oriented (by default) flex containers: */
+ display: flex;
+}
+
+.swatch {
+ box-sizing: border-box;
+
+ /* These represent the 120px borders in the testcase, scaled down 0.4x: */
+ border: 48px solid;
+
+ /* Share the width equally among the swatches. (The height will be
+ automatically set to the flex container's row height, via default
+ 'align-self' behavior.) */
+ flex: 1;
+
+ /* These values come directly from the unwriteableMargin values in the
+ testcase's configuration code in printpreview_helper.xhtml, with
+ each measurement scaled down by exactly 0.4x. The extra 0.1in accounts
+ for the centering of each page in its grid cell. */
+ margin-top: 0.24in;
+ margin-right: calc(0.04in + 0.1in);
+ margin-bottom: 0.16in;
+ margin-left: calc(0.12in + 0.1in);
+}
+.row:nth-child(1) > .swatch:nth-child(1) { border-color: cyan; }
+.row:nth-child(1) > .swatch:nth-child(2) { border-color: yellow; }
+.row:nth-child(2) > .swatch:nth-child(1) { border-color: pink; }
+.row:nth-child(2) > .swatch:nth-child(2) { border-color: orange; }
+</style>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw9.html b/layout/base/tests/chrome/printpreview_pps_uw9.html
new file mode 100644
index 0000000000..9bd0e1f5a4
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw9.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<!-- This is a testcase for a "9-pages-per-sheet" scenario.
+ There are 9 full-page "swatches" with large colorful borders. -->
+<style>
+html, body { margin: 0; height: 100%; }
+.swatch {
+ box-sizing: border-box;
+ border: 120px solid;
+ height: 100%;
+}
+.swatch:nth-child(1) { border-color: cyan; }
+.swatch:nth-child(2) { border-color: yellow; }
+.swatch:nth-child(3) { border-color: pink; }
+.swatch:nth-child(4) { border-color: orange; }
+.swatch:nth-child(5) { border-color: purple; }
+.swatch:nth-child(6) { border-color: olive; }
+.swatch:nth-child(7) { border-color: blue; }
+.swatch:nth-child(8) { border-color: tan; }
+.swatch:nth-child(9) { border-color: fuchsia; }
+</style>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
+<div class="swatch"></div>
diff --git a/layout/base/tests/chrome/printpreview_pps_uw9_ref.html b/layout/base/tests/chrome/printpreview_pps_uw9_ref.html
new file mode 100644
index 0000000000..de3cb1e08b
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_pps_uw9_ref.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<!-- This is a reference case for a "9-pages-per-sheet" scenario. This file is
+ a mockup of a sheet with 9 pages, with the pages all having a 0.2x scale
+ scale factor applied. (We end up with that scale factor by subtracting the
+ requested unwriteable margins from the sheet, and dividing up the
+ remaining space equally among the "virtual pages".) -->
+<style>
+html {
+ display: flex;
+ height: 100%;
+ margin: 0;
+}
+body {
+ /* As a flex item (a child of the html element), fill the available area. */
+ flex: 1;
+
+ /* We lay out the body as a column-oriented flex container (whose children,
+ in turn, are rows). */
+ display: flex;
+ flex-direction: column;
+
+ /* These values come directly from the unwriteableMargin values in the
+ testcase's configuration code in printpreview_helper.xhtml. */
+ margin-top: 0.2in;
+ margin-right: 0.8in;
+ margin-bottom: 0.4in;
+ margin-left: 1.2in;
+}
+
+.row {
+ /* Give each row an equal share of the available height: */
+ flex: 1;
+
+ /* ...and render them as row-oriented (by default) flex containers: */
+ display: flex;
+}
+
+.swatch {
+ box-sizing: border-box;
+
+ /* These represent the 120px borders in the testcase, scaled down 0.2x: */
+ border: 24px solid;
+
+ /* Share the width equally among the swatches. (The height will be
+ automatically set to the flex container's row height, via default
+ 'align-self' behavior.) */
+ flex: 1;
+
+ /* These values come directly from the unwriteableMargin values in the
+ testcase's configuration code in printpreview_helper.xhtml, with
+ each measurement scaled down by exactly 0.2x. The extra 0.5667in accounts
+ for the centering of each page in its grid cell. */
+ margin-top: calc(0.04in + 0.5667in);
+ margin-right: 0.16in;
+ margin-bottom: calc(0.08in + 0.5667in);
+ margin-left: 0.24in;
+}
+.row:nth-child(1) > .swatch:nth-child(1) { border-color: cyan; }
+.row:nth-child(1) > .swatch:nth-child(2) { border-color: yellow; }
+.row:nth-child(1) > .swatch:nth-child(3) { border-color: pink; }
+.row:nth-child(2) > .swatch:nth-child(1) { border-color: orange; }
+.row:nth-child(2) > .swatch:nth-child(2) { border-color: purple; }
+.row:nth-child(2) > .swatch:nth-child(3) { border-color: olive; }
+.row:nth-child(3) > .swatch:nth-child(1) { border-color: blue; }
+.row:nth-child(3) > .swatch:nth-child(2) { border-color: tan; }
+.row:nth-child(3) > .swatch:nth-child(3) { border-color: fuchsia; }
+</style>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
+<div class="row">
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+ <div class="swatch"></div>
+</div>
diff --git a/layout/base/tests/chrome/printpreview_prettyprint.xml b/layout/base/tests/chrome/printpreview_prettyprint.xml
new file mode 100644
index 0000000000..759d5066cf
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_prettyprint.xml
@@ -0,0 +1 @@
+<out>Here be sea hags</out>
diff --git a/layout/base/tests/chrome/printpreview_prettyprint_ref.xhtml b/layout/base/tests/chrome/printpreview_prettyprint_ref.xhtml
new file mode 100644
index 0000000000..7309425fb4
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_prettyprint_ref.xhtml
@@ -0,0 +1,3 @@
+<out><div id="top" xmlns="http://www.w3.org/1999/xhtml"><link href="chrome://global/content/xml/XMLPrettyPrint.css" type="text/css" rel="stylesheet"/><div id="header"><p>
+ This XML file does not appear to have any style information associated with it. The document tree is shown below.
+ </p></div><main id="tree" class="highlight"><div>&lt;<span class="start-tag">out</span>&gt;<span class="text">Here be sea hags</span>&lt;/<span class="end-tag">out</span>&gt;</div></main></div></out>
diff --git a/layout/base/tests/chrome/printpreview_quirks.html b/layout/base/tests/chrome/printpreview_quirks.html
new file mode 100644
index 0000000000..fa8714a0f7
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_quirks.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset="utf-8">
+<style>
+ .HiDdEn { display: none }
+</style>
+<body class="hidden">
+ Some content that I should be able to print.
+</body>
diff --git a/layout/base/tests/chrome/printpreview_quirks_ref.html b/layout/base/tests/chrome/printpreview_quirks_ref.html
new file mode 100644
index 0000000000..4c6fcce1f6
--- /dev/null
+++ b/layout/base/tests/chrome/printpreview_quirks_ref.html
@@ -0,0 +1,5 @@
+<!doctype html>
+<meta charset="utf-8">
+<body>
+ Some content that I should be able to print.
+</body>
diff --git a/layout/base/tests/chrome/red.png b/layout/base/tests/chrome/red.png
new file mode 100644
index 0000000000..57bf3ddc52
--- /dev/null
+++ b/layout/base/tests/chrome/red.png
Binary files differ
diff --git a/layout/base/tests/chrome/test_bug1018265.xhtml b/layout/base/tests/chrome/test_bug1018265.xhtml
new file mode 100644
index 0000000000..53862b0e5e
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug1018265.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1018265
+-->
+<window title="Mozilla Bug 1018265"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="run()">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 1018265 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function run() {
+ window.openDialog("file_bug1018265.xhtml", "contentViewerTest", "chrome,width=100,height=100,noopener", window);
+ }
+
+ function done() {
+ ok(true, "done");
+ setTimeout(function() { SimpleTest.finish(); }, 0);
+ }
+ ]]>
+ </script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1018265"
+ target="_blank">Mozilla Bug 1018265</a>
+ </body>
+</window>
diff --git a/layout/base/tests/chrome/test_bug1041200.xhtml b/layout/base/tests/chrome/test_bug1041200.xhtml
new file mode 100644
index 0000000000..365ecf2825
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug1041200.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ // Run the test in a separate window so that the test runs as a chrome
+ // window
+ window.openDialog("bug1041200_window.html", "bug1041200",
+ "chrome,width=800,height=800,noopener", window);
+ ]]>
+ </script>
+</window>
diff --git a/layout/base/tests/chrome/test_bug396367-1.html b/layout/base/tests/chrome/test_bug396367-1.html
new file mode 100644
index 0000000000..63b33d335d
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug396367-1.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396367
+-->
+<head>
+ <title>Test for Bug 396367</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"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function finish() {
+ ok(true, "didn't crash");
+ top.docShell.browsingContext.textZoom = 1;
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=396367">Mozilla Bug 396367</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+
+<input>
+<script>document.body.setAttribute('style', 'display: -moz-box; overflow: scroll;');</script>
+<script>
+top.docShell.browsingContext.textZoom = Math.floor(10 * Math.random()) / 4 + 0.2;
+document.documentElement.offsetHeight;
+setTimeout(finish, 0);
+</script>
+
+
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_bug396367-2.html b/layout/base/tests/chrome/test_bug396367-2.html
new file mode 100644
index 0000000000..2a751cd8be
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug396367-2.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396367
+-->
+<head>
+ <title>Test for Bug 396367</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"/>
+ <style>select::after { content:"m"; }</style>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function finish() {
+ ok(true, "didn't crash");
+ top.docShell.browsingContext.textZoom = 1;
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=396367">Mozilla Bug 396367</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+
+<div style="overflow: scroll; float: left;">
+
+<select></select>
+
+<li style="display: table-cell;">
+
+<script>
+top.docShell.browsingContext.textZoom = Math.floor(10 * Math.random()) / 4 + 0.2;
+document.documentElement.offsetHeight;
+setTimeout(finish, 0);
+</script>
+</li>
+</div>
+
+
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_bug420499.xhtml b/layout/base/tests/chrome/test_bug420499.xhtml
new file mode 100644
index 0000000000..22fefd7987
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug420499.xhtml
@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=420499
+-->
+<window title="Mozilla Bug 420499" onload="setTimeout(focusInput, 500);"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+
+
+ <menu id="menu" label="Menu">
+ <menupopup id="file-popup">
+ <!-- <input xmlns="http://www.w3.org/1999/xhtml" id="some-text" maxlength="10" value="some text"/> -->
+ <menu label="submenu">
+ <menupopup id="file-popup-inner">
+
+ <menuitem label="Item1"/>
+ <menuitem label="Item2"/>
+ <input xmlns="http://www.w3.org/1999/xhtml" id="some-text" maxlength="10" value="some more text"/>
+ </menupopup>
+ </menu>
+ <menuitem label="Item3"/>
+ <menuitem label="Item4"/>
+ </menupopup>
+ </menu>
+
+ <popupset>
+ <menupopup id="contextmenu">
+ <menuitem label="Cut"/>
+ <menuitem label="Copy"/>
+ <menuitem label="Paste"/>
+ </menupopup>
+ <tooltip id="tooltip" orient="vertical">
+ <description value="This is a tooltip"/>
+ </tooltip>
+ </popupset>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml" bgcolor="white">
+
+ <p id="par1">Paragraph 1</p>
+ <p id="par2">Paragraph 2</p>
+ <p id="par3">Paragraph 3</p>
+ <p id="par4">Paragraph 4</p>
+ <p id="par5">Paragraph 5</p>
+
+ <input type="text" id="text-input" maxlength="10" value="some more text"/> <br />
+
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=420499"
+ target="_blank">Mozilla Bug 420499</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript"><![CDATA[
+
+ /** Test for Bug 420499 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function getSelectionController() {
+ return window.docShell
+ .QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsISelectionDisplay)
+ .QueryInterface(Ci.nsISelectionController);
+ }
+
+ function isCaretVisible() {
+ var docShell = window.docShell;
+ var selCon = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsISelectionDisplay)
+ .QueryInterface(Ci.nsISelectionController);
+ return selCon.caretVisible;
+ }
+
+ function focusInput() {
+ ok(!isCaretVisible(), "Caret shouldn't be visible");
+ $("text-input").focus();
+ ok(isCaretVisible(), "Caret should be visible when input focused");
+ window.addEventListener("popupshown", popupMenuShownHandler);
+ $("menu").open = true;
+ }
+
+ function popupMenuShownHandler() {
+ window.removeEventListener("popupshown", popupMenuShownHandler);
+ ok(!isCaretVisible(), "Caret shouldn't be visible when menu open");
+ window.addEventListener("popuphidden", ensureParagraphFocused);
+ $("menu").open = false;
+ }
+
+ function ensureParagraphFocused() {
+ window.removeEventListener("popuphidden", ensureParagraphFocused);
+ ok(isCaretVisible(), "Caret should have returned to previous focus");
+ window.addEventListener("popupshown", popupMenuShownHandler2);
+ $("contextmenu").openPopup($('text-input'), "topleft" , -1 , -1 , true, true);
+ }
+
+ function popupMenuShownHandler2() {
+ window.removeEventListener("popupshown", popupMenuShownHandler2);
+ ok(isCaretVisible(), "Caret should be visible when context menu open");
+ window.addEventListener("popuphidden", ensureParagraphFocused2);
+ document.getElementById("contextmenu").hidePopup();
+ }
+
+ function ensureParagraphFocused2() {
+ window.removeEventListener("popuphidden", ensureParagraphFocused2);
+ ok(isCaretVisible(), "Caret should still be visible");
+ window.addEventListener("popupshown", tooltipShownHandler);
+ $("tooltip").openPopup($('text-input'), "topleft" , -1 , -1 , false, true);
+ }
+
+ function tooltipShownHandler() {
+ window.removeEventListener("popupshown", tooltipShownHandler);
+ ok(isCaretVisible(), "Caret should be visible when tooltip is visible");
+ window.addEventListener("popuphidden", ensureParagraphFocused3);
+ document.getElementById("tooltip").hidePopup();
+ }
+
+ function ensureParagraphFocused3() {
+ window.removeEventListener("popuphidden", ensureParagraphFocused2);
+ ok(isCaretVisible(), "Caret should still be visible");
+ SimpleTest.finish();
+ }
+ ]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_bug458898.html b/layout/base/tests/chrome/test_bug458898.html
new file mode 100644
index 0000000000..a7913f9a2a
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug458898.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=458898
+-->
+<head>
+ <title>Test for Bug 458898</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"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=458898">Mozilla Bug 458898</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+var win = window.browsingContext.topChromeWindow.openDialog("file_bug458898.html");
+
+function loaded() {
+ var disableWindowResizePref = "dom.disable_window_move_resize";
+ SpecialPowers.pushPrefEnv({"set":[[disableWindowResizePref, false]]}, function() {
+ win.sizeToContent();
+ ok(win.innerWidth >= 100, "innerWidth: " + win.innerWidth + " >= 100 ?");
+ ok(win.innerHeight >= 200, "innerHeight: " + win.innerHeight + " >= 200 ?");
+ win.close();
+ SimpleTest.finish();
+ });
+}
+
+win.addEventListener("load", loaded);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_bug465448.xhtml b/layout/base/tests/chrome/test_bug465448.xhtml
new file mode 100644
index 0000000000..fa9d1589a2
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug465448.xhtml
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window title="Bug 465448"
+ onload="loaded()"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<script><![CDATA[
+SimpleTest.waitForExplicitFinish();
+var loadedCalled = false;
+var win = window.open("file_bug465448.html", "_blank", "width=600,height=600");
+
+function loaded() {
+ if (!loadedCalled) {
+ loadedCalled = true;
+ return;
+ }
+ win.sizeToContent();
+ win.sizeToContent();
+ win.sizeToContent();
+ win.sizeToContent();
+ win.sizeToContent();
+ win.sizeToContent();
+ ok(win.innerWidth >= 100, "innerWidth");
+ ok(win.innerHeight >= 200, "innerHeight");
+ win.close();
+ SimpleTest.finish();
+}
+]]></script>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<p id="display">
+</p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+</body>
+
+</window>
diff --git a/layout/base/tests/chrome/test_bug514660.xhtml b/layout/base/tests/chrome/test_bug514660.xhtml
new file mode 100644
index 0000000000..a7c03a054d
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug514660.xhtml
@@ -0,0 +1,35 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514660
+-->
+<window title="Mozilla Bug 504311"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="doTest()">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=514660"
+ target="_blank">Mozilla Bug 514660</a>
+<textarea></textarea>
+</body>
+ <!-- test code goes here -->
+<script type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function doTest()
+{
+ var viewer = window.docShell.contentViewer;
+ viewer.authorStyleDisabled = true;
+
+ document.documentElement.getBoundingClientRect();
+ ok(true, "Didn't crash");
+
+ viewer.authorStyleDisabled = false;
+
+ SimpleTest.finish();
+}
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_bug533845.xhtml b/layout/base/tests/chrome/test_bug533845.xhtml
new file mode 100644
index 0000000000..3ea21cb9b7
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug533845.xhtml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=533845
+-->
+<window title="Mozilla Bug 533845"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="doTest()">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<panel id="panel" style="width: 500px; height: 500px">
+ <iframe type="content" id="contentFrame" src="data:text/html,&lt;html&gt;&lt;body onclick='document.body.textContent=1'&gt;This is a panel!&lt;/body&gt;&lt;/html&gt;" width="500" height="500"/>
+</panel>
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=533845"
+ target="_blank">Mozilla Bug 533845</a>
+</body>
+ <!-- test code goes here -->
+<script type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+function doTest() {
+ let panel = document.getElementById("panel");
+ panel.addEventListener("popupshown", function onpopupshown() {
+ continueTest();
+ panel.addEventListener("popuphidden", function onpopuphidden() {
+ SimpleTest.finish();
+ }, { once: true });
+ panel.hidePopup();
+ }, { once: true });
+ panel.openPopup();
+}
+
+function continueTest() {
+ var ifrwindow = document.getElementById("contentFrame").contentWindow;
+ ifrwindow.focus();
+ var utils = ifrwindow.windowUtils;
+ var rect = ifrwindow.document.body.getBoundingClientRect();
+ var x = rect.left + (rect.width/2);
+ var y = rect.top + (rect.height/2);
+ utils.sendMouseEvent("mousedown", x, y, 0, 1, 0);
+ utils.sendMouseEvent("mouseup", x, y, 0, 1, 0);
+ is(ifrwindow.document.body.textContent, "1", "Should have got a click event!");
+}
+
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_bug551434.html b/layout/base/tests/chrome/test_bug551434.html
new file mode 100644
index 0000000000..5e21cf6408
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug551434.html
@@ -0,0 +1,95 @@
+<html>
+<head>
+ <title>Test for Bug 551434</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
+</head>
+<body>
+</div>
+<pre id="test">
+<input id="i1" onkeydown="gKeyDown1++; $('i2').focus();" onkeypress="gKeyPress1++;" onkeyup="gKeyUp1++;"/>
+<input id="i2" onkeydown="gKeyDown2++;" onkeypress="gKeyPress2++;" onkeyup="gKeyUp2++;"/>
+
+<input id="i3" onkeydown="gKeyDown3++; frames[0].document.getElementById('i4').focus();"
+ onkeypress="gKeyPress3++;" onkeyup="gKeyUp3++;"/>
+<iframe id="iframe" src="http://example.org/chrome/layout/base/tests/chrome/bug551434_childframe.html"></iframe>
+
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var gKeyDown1 = 0, gKeyPress1 = 0, gKeyUp1 = 0;
+var gKeyDown2 = 0, gKeyPress2 = 0, gKeyUp2 = 0;
+var gKeyDown3 = 0, gKeyPress3 = 0, gKeyUp3 = 0;
+
+function runTest()
+{
+ $("i1").focus();
+
+ // key events should not be retargeted when the focus changes to an
+ // element in the same document.
+ synthesizeKey("a", {type: "keydown"});
+ is(document.activeElement, $("i2"), "input 2 in focused");
+
+ synthesizeKey("a", {type: "keyup"});
+
+ is(gKeyDown1, 1, "keydown on input 1");
+ is(gKeyPress1, 0, "keypress on input 1");
+ is(gKeyUp1, 0, "keyup on input 1");
+ is(gKeyDown2, 0, "keydown on input 2");
+ is(gKeyPress2, 1, "keypress on input 2");
+ is(gKeyUp2, 1, "keyup on input 2");
+
+ is($("i1").value, "", "input 1 value");
+ is($("i2").value, "a", "input 2 value");
+
+ // key events should however be retargeted when the focus changes to an
+ // element in the a content document from a chrome document.
+ $("i3").focus();
+
+ var childWinObj = frames[0].wrappedJSObject;
+
+ sendString("b");
+ is(gKeyDown3, 1, "keydown on input 3");
+ is(gKeyPress3, 1, "keypress on input 3");
+ is(gKeyUp3, 1, "keyup on input 3");
+ is(childWinObj.gKeyDownChild, 0, "keydown on input 4");
+ is(childWinObj.gKeyPressChild, 0, "keypress on input 4");
+ is(childWinObj.gKeyUpChild, 0, "keyup on input 4");
+
+ var i4 = frames[0].document.getElementById("i4");
+ is($("i3").value, "b", "input 3 value");
+ is(i4.value, "", "input 4 value");
+
+ is(document.activeElement, $("iframe"), "parent focus");
+ is(frames[0].document.activeElement, i4, "child focus");
+
+ // key events should also be retargeted when the focus changes to an
+ // element in a chrome document from a content document.
+ i4.addEventListener("keydown", () => $("i3").focus());
+
+ sendString("c");
+
+ is(gKeyDown3, 1, "keydown on input 3");
+ is(gKeyPress3, 1, "keypress on input 3");
+ is(gKeyUp3, 1, "keyup on input 3");
+ is(childWinObj.gKeyDownChild, 1, "keydown on input 4");
+ is(childWinObj.gKeyPressChild, 1, "keypress on input 4");
+ is(childWinObj.gKeyUpChild, 1, "keyup on input 4");
+
+ is($("i3").value, "b", "input 3 value");
+ is(i4.value, "c", "input 4 value");
+
+ is(document.activeElement, $("i3"), "parent focus");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(runTest);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/chrome/test_bug708062.html b/layout/base/tests/chrome/test_bug708062.html
new file mode 100644
index 0000000000..ee7df7d37d
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug708062.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=708062
+-->
+<head>
+ <title>Test for Bug 708062</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 onload="doTest()">
+
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=708062">Mozilla Bug 708062</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<iframe id="f" style="width:100px;"
+ src="data:text/html,A<div id='d' style='position:fixed;width:170px;top:0;right:0;height:1px;background:yellow;'>"></iframe>
+<pre id="test">
+
+<script>
+function isBoundingClientRect(e, r, msg) {
+ var BCR = e.getBoundingClientRect();
+ is([BCR.left, BCR.top, BCR.right, BCR.bottom].join(','), r, msg);
+}
+
+function doTest() {
+ var f = document.getElementById('f');
+
+ var d = f.contentDocument.getElementById('d');
+
+ isBoundingClientRect(d, "-70,0,100,1", "initial rect");
+ SpecialPowers.setFullZoom(f.contentWindow, 2);
+ isBoundingClientRect(d, "-120,0,50,1", "after zooming in");
+ SpecialPowers.setFullZoom(f.contentWindow, 1);
+ isBoundingClientRect(d, "-70,0,100,1", "after zooming back out");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_bug812817.xhtml b/layout/base/tests/chrome/test_bug812817.xhtml
new file mode 100644
index 0000000000..c5eb747ea6
--- /dev/null
+++ b/layout/base/tests/chrome/test_bug812817.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=812817
+-->
+<window title="Mozilla Bug 812817"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="doTest()">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<panel id="panel" width="200" height="200" onpopupshown="continueTest()">
+</panel>
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=812817"
+ target="_blank">Mozilla Bug 812817</a>
+</body>
+ <!-- test code goes here -->
+<script type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+
+var panel = document.getElementById('panel');
+function doTest() {
+ panel.openPopup(null, '', 500, 500, false, false, null);
+}
+
+function continueTest() {
+ panel.style.background = "url(blue-32x32.png)";
+ setTimeout(function() {
+ ok(true, "Didn't crash");
+ SimpleTest.finish();
+ }, 50);
+}
+
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_chrome_content_integration.xhtml b/layout/base/tests/chrome/test_chrome_content_integration.xhtml
new file mode 100644
index 0000000000..6e6be4761a
--- /dev/null
+++ b/layout/base/tests/chrome/test_chrome_content_integration.xhtml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ // Run the test in a separate window so that the test runs as a chrome
+ // window
+ var root = getRootDirectory(window.location.href);
+ window.openDialog(root + "chrome_content_integration_window.xhtml", "chrome_content_integration",
+ "chrome,width=200,height=300,noopener", window);
+ ]]>
+ </script>
+</window>
diff --git a/layout/base/tests/chrome/test_color_scheme_browser.xhtml b/layout/base/tests/chrome/test_color_scheme_browser.xhtml
new file mode 100644
index 0000000000..15a6ac2282
--- /dev/null
+++ b/layout/base/tests/chrome/test_color_scheme_browser.xhtml
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <head>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/chrome-harness.js"></script>
+ <style>
+ #light { color-scheme: light }
+ #dark { color-scheme: dark }
+ </style>
+ </head>
+ <body>
+ <div id="dynamic-test">
+ <xul:browser type="content" remote="true" nodefaultsrc="true" class="remote" />
+ <xul:browser type="content" src="about:blank" class="nonremote" />
+ </div>
+ <div id="light">
+ <xul:browser type="content" remote="true" nodefaultsrc="true" class="remote" />
+ <xul:browser type="content" src="about:blank" class="nonremote" />
+ </div>
+ <div id="dark">
+ <xul:browser type="content" remote="true" nodefaultsrc="true" class="remote" />
+ <xul:browser type="content" src="about:blank" class="nonremote" />
+ </div>
+ <script><![CDATA[
+ // FIXME: This shouldn't be needed if remote browsers would block the load event.
+ add_task(async function ensureBrowsersLoaded() {
+ const triggeringPrincipal = document.nodePrincipal;
+ for (let b of document.querySelectorAll("browser[remote=true]")) {
+ let loaded = new Promise(resolve => {
+ b.addProgressListener({
+ onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
+ info(`Got state change for ${b.parentNode.id}: ${aStateFlags}, ${aStatus}`);
+ if (
+ aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW &&
+ aStateFlags & Ci.nsIWebProgressListener.STATE_STOP
+ ) {
+ resolve();
+ b.removeProgressListener(this);
+ }
+ },
+ QueryInterface: ChromeUtils.generateQI([
+ "nsIWebProgressListener",
+ "nsISupportsWeakReference",
+ ]),
+ });
+ });
+ b.loadURI(null /*blank*/, { triggeringPrincipal });
+ await loaded;
+ }
+ });
+ async function getBrowserColorScheme(browser) {
+ return SpecialPowers.spawn(browser, [], () => {
+ return content.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
+ });
+ }
+ async function tick() {
+ return new Promise(resolve => {
+ requestAnimationFrame(() => requestAnimationFrame(resolve));
+ });
+ }
+ async function testElement(id, expectedIfTop, expectedIfNonTop) {
+ let element = document.getElementById(id);
+ for (let browser of element.querySelectorAll("browser")) {
+ let scheme = await getBrowserColorScheme(browser);
+ let expected = browser.browsingContext.top == browser.browsingContext ? expectedIfTop : expectedIfNonTop;
+ is(scheme, expected, `${id}: ${browser.className} should be ${expected}`);
+ }
+ }
+ add_task(async function test_browser_color_scheme() {
+ let current = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
+ for (let id of ["dynamic-test", "light", "dark"]) {
+ let expected = id == "dynamic-test" ? current : id;
+ await testElement(id, expected, expected);
+ }
+ });
+
+ add_task(async function test_browser_color_scheme_dynamic_style() {
+ let current = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
+ let dynamicTest = document.getElementById("dynamic-test");
+ for (let value of ["light", "dark"]) {
+ await tick();
+ dynamicTest.style.colorScheme = value;
+ await testElement("dynamic-test", value, value);
+ }
+ dynamicTest.style.colorScheme = "";
+ await tick();
+ });
+
+ add_task(async function test_browser_color_scheme_dynamic_system() {
+ const ACTIVE_THEME_ID = Services.prefs.getCharPref("extensions.activeThemeID");
+ const DEFAULT_THEME_ID = "default-theme@mozilla.org";
+ if (ACTIVE_THEME_ID != DEFAULT_THEME_ID) {
+ info(`skipping test_browser_color_scheme_dynamic_system because the active theme is ${ACTIVE_THEME_ID} instead of ${DEFAULT_THEME_ID}`);
+ return;
+ }
+ for (let dark of [true, false]) {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["layout.css.prefers-color-scheme.content-override", 2],
+ ["ui.systemUsesDarkTheme", dark ? 1 : 0],
+ ]
+ });
+ await tick();
+ let expected = dark ? "dark" : "light";
+ await testElement("dynamic-test", expected, expected);
+ await SpecialPowers.popPrefEnv();
+ }
+ });
+ ]]></script>
+ </body>
+</html>
+
diff --git a/layout/base/tests/chrome/test_css_visibility_propagation.xhtml b/layout/base/tests/chrome/test_css_visibility_propagation.xhtml
new file mode 100644
index 0000000000..7b1b082064
--- /dev/null
+++ b/layout/base/tests/chrome/test_css_visibility_propagation.xhtml
@@ -0,0 +1,209 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+<body xmlns="http://www.w3.org/1999/xhtml"></body>
+<script>
+<![CDATA[
+const baseURL = "chrome://mochitests/content/chrome/layout/base/tests/chrome/";
+
+function checkHiddenEmbeddederState(window1, window2, expected1, expected2)
+{
+ ok(!window1.browsingContext.isUnderHiddenEmbedderElement, "window1 visible state");
+ ok(!window2.browsingContext.isUnderHiddenEmbedderElement, "window2 visible state");
+ is(window1.document.querySelector("browser").contentWindow.browsingContext.isUnderHiddenEmbedderElement, !expected1,
+ "window1 child visible state");
+ is(window2.document.querySelector("browser").contentWindow.browsingContext.isUnderHiddenEmbedderElement, !expected2,
+ "window2 child visible state");
+}
+
+// Tests that browser visibility is updated when it's swapped.
+add_task(async () => {
+ // Open two new windows to swap iframes.
+ const window1 = window.browsingContext.topChromeWindow.open(
+ baseURL + "window_css_visibility_propagation-1.xhtml",
+ "_blank", "chrome");
+ const window2 = window.browsingContext.topChromeWindow.open(
+ baseURL + "window_css_visibility_propagation-2.xhtml",
+ "_blank", "chrome");
+
+ const loadWindow1 =
+ new Promise(resolve => window1.addEventListener("load", resolve));
+ const loadWindow2 =
+ new Promise(resolve => window2.addEventListener("load", resolve));
+
+ await Promise.all([ loadWindow1, loadWindow2 ]);
+
+ checkHiddenEmbeddederState(window1, window2, true, true);
+
+ // Hide the parent of browser2.
+ let parent = window2.document.getElementById("parent");
+ parent.style.visibility = "hidden";
+ parent.getBoundingClientRect();
+
+ checkHiddenEmbeddederState(window1, window2, true, false);
+
+ const browser2 = window2.document.querySelector("browser");
+ let target = browser2.contentDocument.getElementById("button");
+ target.focus();
+
+ // browser2 is now in a visibility:hidden element in window2,
+ // so that Element.focus() shouldn't work.
+ isnot(browser2.contentDocument.activeElement, target,
+ "Element.focus() shouldn't work in invisible browser");
+
+ // Make the parent visible.
+ parent.style.visibility = "";
+ parent.getBoundingClientRect();
+
+ checkHiddenEmbeddederState(window1, window2, true, true);
+
+ target.focus();
+
+ // browser2 is visible now, so focus() should work.
+ is(browser2.contentDocument.activeElement, target,
+ "Element.focus() should work in visible browser");
+
+ target.blur();
+ isnot(browser2.contentDocument.activeElement, target,
+ "The target element shouldn't be activeElement");
+
+ // Swap the content in browser1 for the content in browser2.
+ const browser1 = window1.document.querySelector("browser");
+ browser1.swapFrameLoaders(browser2);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ target = browser1.contentDocument.getElementById("button");
+ target.focus();
+
+ // browser1 is in a visibility:hidden element in window1,
+ // so that Element.focus() shouldn't work.
+ isnot(browser1.contentDocument.activeElement, target,
+ "Element.focus() shouldn't work in invisible browser");
+
+ checkHiddenEmbeddederState(window1, window2, false, true);
+
+ parent = window1.document.getElementById("parent");
+ parent.style.visibility = "visible";
+ parent.getBoundingClientRect();
+
+ checkHiddenEmbeddederState(window1, window2, true, true);
+
+ target.focus();
+
+ // Now browser1 is in a visibility:visible element, so that
+ // Element.focus() should just work.
+ is(browser1.contentDocument.activeElement, target,
+ "Element.focus() should work in visible browser");
+
+ window1.close();
+ window2.close();
+});
+
+// Tests that ancestor's visibility change doesn't clobber child
+// iframe's visibility if the child iframe is hidden by an
+// element in the ancestor document.
+add_task(async () => {
+ const tabReady = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if (event.data == "ready") {
+ resolve();
+ }
+ }, { once: true });
+ });
+ const tabWindow =
+ window.open(baseURL + "window_css_visibility_propagation-3.html");
+ await tabReady;
+
+ const childIFrame = tabWindow.document.querySelector("iframe");
+
+ const grandChildBrowser =
+ childIFrame.contentDocument.querySelector("browser");
+ let target = grandChildBrowser.contentDocument.getElementById("button");
+ target.focus();
+
+ ok(!tabWindow.browsingContext.isUnderHiddenEmbedderElement, "tab window is visible");
+ ok(!childIFrame.browsingContext.isUnderHiddenEmbedderElement, "iframe is visible");
+ ok(!grandChildBrowser.browsingContext.isUnderHiddenEmbedderElement, "grandchild is visible");
+
+ is(grandChildBrowser.contentDocument.activeElement, target,
+ "Element.focus() should work in visible browser");
+ target.blur();
+
+ // Hide the parent element of the grand child browser.
+ let parent = childIFrame.contentDocument.getElementById("parent");
+ parent.style.visibility = "hidden";
+ parent.getBoundingClientRect();
+
+ ok(!tabWindow.browsingContext.isUnderHiddenEmbedderElement, "tab window is visible");
+ ok(!childIFrame.browsingContext.isUnderHiddenEmbedderElement, "iframe is visible");
+ ok(grandChildBrowser.browsingContext.isUnderHiddenEmbedderElement, "grandchild is not visible");
+
+ target.focus();
+
+ isnot(grandChildBrowser.contentDocument.activeElement, target,
+ "Element.focus() shouldn't work in invisible browser");
+
+ // Hide the parent element of the child iframe.
+ parent = tabWindow.document.getElementById("parent");
+ parent.style.visibility = "hidden";
+ parent.getBoundingClientRect();
+
+ target.focus();
+
+ ok(!tabWindow.browsingContext.isUnderHiddenEmbedderElement, "tab window is visible");
+ ok(childIFrame.browsingContext.isUnderHiddenEmbedderElement, "iframe is not visible");
+ ok(grandChildBrowser.browsingContext.isUnderHiddenEmbedderElement, "grandchild is not visible");
+
+ isnot(grandChildBrowser.contentDocument.activeElement, target,
+ "Element.focus() shouldn't work in invisible iframe");
+
+ // Make the parent element of the child iframe visible.
+ parent.style.visibility = "visible";
+ parent.getBoundingClientRect();
+
+ ok(!tabWindow.browsingContext.isUnderHiddenEmbedderElement, "tab window is visible");
+ ok(!childIFrame.browsingContext.isUnderHiddenEmbedderElement, "iframe is visible");
+ ok(grandChildBrowser.browsingContext.isUnderHiddenEmbedderElement, "grandchild is not visible");
+
+ target.focus();
+
+ // Even if the child iframe is visible, but still the grand child is
+ // hidden by the parent element of the grand child browser so that
+ // we can't focus to the element in the grand child browser.
+ isnot(grandChildBrowser.contentDocument.activeElement, target,
+ "Element.focus() shouldn't work in invisible browser");
+
+ tabWindow.close();
+});
+
+// Tests that an iframe is initially hidden by a visibility:hidden element in
+// the parent document.
+add_task(async () => {
+ const tabReady = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ if (event.data == "ready") {
+ resolve();
+ }
+ }, { once: true });
+ });
+ const tabWindow =
+ window.open(baseURL + "window_css_visibility_propagation-4.html");
+ await tabReady;
+
+ const iframe = tabWindow.document.querySelector("iframe");
+ let target = iframe.contentDocument.getElementById("button");
+ target.focus();
+
+ ok(!tabWindow.browsingContext.isUnderHiddenEmbedderElement, "tab window is visible");
+ ok(iframe.browsingContext.isUnderHiddenEmbedderElement, "iframe is not visible");
+
+ isnot(iframe.contentDocument.activeElement, target,
+ "Element.focus() shouldn't work in invisible iframe");
+
+ tabWindow.close();
+});
+]]>
+</script>
+</window>
diff --git a/layout/base/tests/chrome/test_default_background.xhtml b/layout/base/tests/chrome/test_default_background.xhtml
new file mode 100644
index 0000000000..26e28c574e
--- /dev/null
+++ b/layout/base/tests/chrome/test_default_background.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+
+ <!-- test results are displayed in the html:body -->
+ <body xmlns="http://www.w3.org/1999/xhtml">
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ SimpleTest.waitForExplicitFinish();
+ // Run the test in a separate window so that the test runs as a chrome
+ // window
+ window.openDialog("default_background_window.xhtml", "default_background",
+ "chrome,width=200,height=300,noopener", window);
+ ]]>
+ </script>
+</window>
diff --git a/layout/base/tests/chrome/test_dialog_with_positioning.html b/layout/base/tests/chrome/test_dialog_with_positioning.html
new file mode 100644
index 0000000000..db08a3d9b4
--- /dev/null
+++ b/layout/base/tests/chrome/test_dialog_with_positioning.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test positioning of fixed-pos/abs-pos elements in a XUL dialog</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/chrome-harness.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var root = getRootDirectory(window.location.href);
+window.openDialog(root + "dialog_with_positioning_window.xhtml", "dialog_with_positioning",
+ "dialog,chrome,noopener", window);
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_document_adopted_styles.html b/layout/base/tests/chrome/test_document_adopted_styles.html
new file mode 100644
index 0000000000..f2784bd60b
--- /dev/null
+++ b/layout/base/tests/chrome/test_document_adopted_styles.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset="utf-8">
+<div class="target"></div>
+<script>
+ const sheet = new CSSStyleSheet();
+ document.adoptedStyleSheets = [sheet];
+ sheet.replaceSync(".target { width: 100px; height: 100px; border-style: solid; border-color: blue; }");
+</script>
diff --git a/layout/base/tests/chrome/test_document_adopted_styles_ref.html b/layout/base/tests/chrome/test_document_adopted_styles_ref.html
new file mode 100644
index 0000000000..0b592207f3
--- /dev/null
+++ b/layout/base/tests/chrome/test_document_adopted_styles_ref.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<meta charset="utf-8">
+<body>
+ <div class="target"></div>
+ <style> .target { width: 100px; height: 100px; border-style: solid; border-color: blue; } </style>
+</body>
diff --git a/layout/base/tests/chrome/test_fixed_bg_scrolling_repaints.html b/layout/base/tests/chrome/test_fixed_bg_scrolling_repaints.html
new file mode 100644
index 0000000000..9543340d6b
--- /dev/null
+++ b/layout/base/tests/chrome/test_fixed_bg_scrolling_repaints.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that we don't get unnecessary repaints with fixed backgrounds</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<!-- Need a timeout here to allow paint unsuppression before we start the test -->
+<body onload="setTimeout(startTest,0)" style="background:url(blue-32x32.png) top left no-repeat fixed; background-size: 100px 2000px; overflow:hidden;">
+<div style="height: 2048px"></div>
+
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var utils = window.windowUtils;
+
+function startTest() {
+ // Do a scroll to ensure we trigger activity heuristics.
+ document.documentElement.scrollTop = 1;
+ waitForAllPaintsFlushed(function () {
+ document.documentElement.scrollTop = 0;
+ waitForAllPaintsFlushed(function () {
+ // Clear paint state and scroll down
+ utils.checkAndClearPaintedState(document.documentElement);
+ document.documentElement.scrollTop = 100;
+ waitForAllPaintsFlushed(function () {
+ // Make sure nothing painted
+ var painted = utils.checkAndClearPaintedState(document.documentElement);
+ is(painted, false, "Fixed background should not have been painted when scrolled");
+ SimpleTest.finish();
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_getClientRectsAndTexts.html b/layout/base/tests/chrome/test_getClientRectsAndTexts.html
new file mode 100644
index 0000000000..d2fdde2197
--- /dev/null
+++ b/layout/base/tests/chrome/test_getClientRectsAndTexts.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <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>
+
+<div id="div1" style="width:200px">Here is some text that <a href="#">will wrap</a> in <a href="#">this small</a>-ish container.</div>
+<div id="div2">Into another <a href="#">container</a></div>
+<div id="div3">A very <span>deep <span>deep <span>deep</span></span></span> bit of text.</div>
+
+<script>
+if (typeof(is) == "undefined") {
+ var is = function(a, b, m) {
+ if(a != b) {
+ window.console.log("Expected '" + b + "' but got '" + a + "': " + m);
+ }
+ };
+}
+
+if (typeof(todo_is) == "undefined") {
+ var todo_is = is;
+}
+
+function testRangeTexts(startNode, startOffset, endNode, endOffset, expectedText, todo) {
+ let r = new Range();
+ r.setStart(startNode, startOffset);
+ r.setEnd(endNode, endOffset);
+
+ let texts = r.getClientRectsAndTexts().textList;
+ let concatText = "";
+ for (let i = 0; i < texts.length; i++) {
+ concatText += texts[i];
+ }
+
+ if (todo) {
+ todo_is(concatText, expectedText, "Text matches.");
+ } else {
+ is(concatText, expectedText, "Text matches.");
+ }
+}
+
+let d1c1 = div1.firstChild;
+let d1c2 = d1c1.nextSibling;
+let d1c3 = d1c2.nextSibling;
+let d1c4 = d1c3.nextSibling;
+let d1c5 = d1c4.nextSibling;
+
+let link1 = d1c2.firstChild;
+let link2 = d1c4.firstChild;
+
+let d2c1 = div2.firstChild;
+let d2c2 = d2c1.nextSibling;
+
+let link3 = d2c2.firstChild;
+
+let d3c1 = div3.firstChild;
+let d3c2 = d3c1.nextSibling;
+let d3c3 = d3c2.nextSibling;
+
+let data = [
+ [d1c1, 0, d1c1, 0, ""],
+ [d1c1, 0, d1c1, 4, "Here"],
+ [d1c1, 4, d1c1, 7, " is"],
+ [d1c1, 22, link1, 0, " "],
+ [d1c1, 22, link1, 1, " w"],
+ [d1c1, 22, d1c3, 1, " will wrap "],
+ [link1, 2, link2, 3, "ll wrap in thi"],
+ [link2, 5, link3, 3, "small-ish container.Into another con"],
+ [d3c1, 3, d3c3, 4, "ery deep deep deep bit"],
+];
+
+data.forEach(function (d) { testRangeTexts.apply(null, d); });
+
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_get_printer_basic_attributes.html b/layout/base/tests/chrome/test_get_printer_basic_attributes.html
new file mode 100644
index 0000000000..26a04e09d5
--- /dev/null
+++ b/layout/base/tests/chrome/test_get_printer_basic_attributes.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body onload="run()">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+async function run() {
+ try {
+ let printerList = Cc["@mozilla.org/gfx/printerlist;1"].getService(
+ Ci.nsIPrinterList
+ );
+
+ let printers = await printerList.printers;
+ for (let printer of printers) {
+ printer.QueryInterface(Ci.nsIPrinter);
+ info(`Listing basic attributes for ${printer.name}:`);
+ let [supportsDuplex, supportsColor] = await Promise.all([printer.supportsDuplex, printer.supportsColor]);
+ info(`* supportsDuplex: ${supportsDuplex}`);
+ info(`* supportsColor: ${supportsColor}`);
+ }
+
+ ok(true, "Retrieved printer basic attributes successfully.");
+ } catch (e) {
+ ok(false, `Error thrown while retrieving printer basic attributes: ${e}.`);
+ console.error(e);
+ }
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_get_printer_orientation.html b/layout/base/tests/chrome/test_get_printer_orientation.html
new file mode 100644
index 0000000000..1bb50eef65
--- /dev/null
+++ b/layout/base/tests/chrome/test_get_printer_orientation.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body onload="run()">
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+async function run() {
+ try {
+ let printerList = Cc["@mozilla.org/gfx/printerlist;1"].getService(
+ Ci.nsIPrinterList
+ );
+ var settingsSvc = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
+ Ci.nsIPrintSettingsService
+ );
+
+ let printers = await printerList.printers;
+ for (let printer of printers) {
+ printer.QueryInterface(Ci.nsIPrinter);
+ let printerInfo = await printer.printerInfo;
+
+ // Look up the printer's defaultSettings:
+ let defaultSettings = printerInfo.defaultSettings;
+
+ // Let the printer impose its defaults onto a fresh settings object:
+ let freshSettings = settingsSvc.createNewPrintSettings();
+ printerList.initPrintSettingsFromPrinter(printer.name, freshSettings);
+
+ // Make sure they agree on the default orientation:
+ is(freshSettings.orientation, defaultSettings.orientation,
+ "initPrintSettingsFromPrinter should produce the same orientation " +
+ "as the printer's defaultSettings");
+ }
+
+ // This ok() just lets us avoid failure-due-to-no-tests-being-run, on
+ // devices that have no printers available & hence skip the loop above:
+ ok(true, "Finished traversing printers.");
+ } catch (e) {
+ ok(false, `Error thrown while retrieving printer info: ${e}.`);
+ console.error(e);
+ }
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_get_printer_paper_sizes.html b/layout/base/tests/chrome/test_get_printer_paper_sizes.html
new file mode 100644
index 0000000000..4ebe462ac6
--- /dev/null
+++ b/layout/base/tests/chrome/test_get_printer_paper_sizes.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body onload="run()">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+async function run() {
+ try {
+ let printerList = Cc["@mozilla.org/gfx/printerlist;1"].getService(
+ Ci.nsIPrinterList
+ );
+ let printers = await printerList.printers;
+ if (printers.length == 0) {
+ ok(true, "There were no printers to iterate through.");
+ }
+
+ for (let printer of printers) {
+ printer.QueryInterface(Ci.nsIPrinter);
+ is(typeof(printer.name), 'string', "Printer name should be a string.");
+ isnot(printer.name, "", "Printer name should never be empty.");
+
+ info(printer.name);
+ info("duplex(" + printer.supportsDuplex + ")");
+
+ let printerInfo = await printer.printerInfo;
+ for (let paper of printerInfo.paperList) {
+ paper.QueryInterface(Ci.nsIPaper);
+
+ info(`${paper.name}: ${paper.width}x${paper.height}`);
+
+ is(typeof(paper.name), 'string', "Paper name should be a string.");
+ isnot(paper.name, "", "Paper name should never be empty.");
+
+ is(typeof(paper.width), 'number', "Paper width should be a number.");
+ ok(paper.width > 0.0, "Paper width should be greater than zero.");
+
+ is(typeof(paper.height), 'number', "Paper height should be a number.");
+ ok(paper.height > 0.0, "Paper height should be greater than zero.");
+
+ let margin = await paper.unwriteableMargin;
+ margin.QueryInterface(Ci.nsIPaperMargin);
+
+ info(`with margin: ${margin.top} ${margin.right} ${margin.bottom} ${margin.left}`);
+
+ is(typeof(margin.top), 'number', "Paper unwriteable margin top should be a number.");
+ is(typeof(margin.right), 'number', "Paper unwriteable margin right should be a number.");
+ is(typeof(margin.bottom), 'number', "Paper unwriteable margin bottom should be a number.");
+ is(typeof(margin.left), 'number', "Paper unwriteable margin left should be a number.");
+
+ ok(margin.top >= 0.0, "Paper unwriteable margin top should be greater than or equal to zero.");
+ ok(margin.right >= 0.0, "Paper unwriteable margin right should be greater than or equal to zero.");
+ ok(margin.bottom >= 0.0, "Paper unwriteable bottom right should be greater than or equal to zero.");
+ ok(margin.left >= 0.0, "Paper unwriteable margin left should be greater than or equal to zero.");
+ }
+ }
+ } catch (e) {
+ ok(false, `Shouldn't throw: ${e}`);
+ console.error(e);
+ }
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_prerendered_transforms.html b/layout/base/tests/chrome/test_prerendered_transforms.html
new file mode 100644
index 0000000000..3151d32413
--- /dev/null
+++ b/layout/base/tests/chrome/test_prerendered_transforms.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that active transformed elements coming into view are prerendered so we don't have to redraw constantly</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<body onload="startTest()">
+<div>
+<div id="t" style="position:absolute; left:0; top:500px; -moz-transform: translatex(-100px); width:200px; height:100px; background:yellow;">
+ <div style="text-align:right">Hello</div>
+ <div style="text-align:left">Kitty</div>
+</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var t = document.getElementById("t");
+var utils = window.windowUtils;
+
+function startTest() {
+ // Do a couple of transform changes to ensure we've triggered activity heuristics
+ waitForAllPaintsFlushed(function () {
+ t.style.MozTransform = "translatex(-75px)";
+ waitForAllPaintsFlushed(function () {
+ t.style.MozTransform = "translatex(-50px)";
+ waitForAllPaintsFlushed(function () {
+ // Clear paint state now and move again.
+ utils.checkAndClearPaintedState(t);
+ // Don't move to 0 since that might trigger some special case that turns off transforms.
+ t.style.MozTransform = "translatex(-1px)";
+ waitForAllPaintsFlushed(function () {
+ var painted = utils.checkAndClearPaintedState(t);
+ is(painted, false, "Transformed element should not have been painted");
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_printer_default_settings.html b/layout/base/tests/chrome/test_printer_default_settings.html
new file mode 100644
index 0000000000..8fb6f98a4e
--- /dev/null
+++ b/layout/base/tests/chrome/test_printer_default_settings.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body onload="run()">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+async function run() {
+ try {
+ let printerList = Cc["@mozilla.org/gfx/printerlist;1"].getService(
+ Ci.nsIPrinterList
+ );
+ let printers = await printerList.printers;
+ if (printers.length == 0) {
+ ok(true, "There were no printers to iterate through.");
+ }
+
+ for (let printer of printers) {
+ printer.QueryInterface(Ci.nsIPrinter);
+ info(printer.name);
+ info("duplex(" + await printer.supportsDuplex + ")");
+
+ const printerInfo = await printer.printerInfo;
+ const settings = printerInfo.defaultSettings;
+ settings.QueryInterface(Ci.nsIPrintSettings);
+
+ is(typeof settings.printerName, "string", "Printer name should be a string.");
+ is(settings.printerName, printer.name, "Print settings' printer should match the printer that created them.");
+
+ is(typeof settings.paperId, "string", "Paper ID should never be null.");
+ is(typeof settings.paperWidth, "number", "Paper width should never be null.");
+ is(typeof settings.paperHeight, "number", "Paper height should never be null.");
+
+ if (settings.paperId != "") {
+ info(`Paper: ${settings.paperId}`);
+ info(`Size: (${settings.paperWidth} x ${settings.paperHeight})`);
+ ok(settings.paperWidth > 0.0, "Paper width should be greater than zero.");
+ ok(settings.paperHeight > 0.0, "Paper height should be greater than zero.");
+ }
+
+ ok(settings.marginTop >= 0.0, "Paper margins should be greater than or equal to zero.");
+ ok(settings.marginRight >= 0.0, "Paper margins should be greater than or equal to zero.");
+ ok(settings.marginBottom >= 0.0, "Paper margins should be greater than or equal to zero.");
+ ok(settings.marginLeft >= 0.0, "Paper margins should be greater than or equal to zero.");
+
+ is(settings.printInColor, await printer.supportsColor, "Print settings' color mode should match the printer's color support.");
+
+ ok(settings.isInitializedFromPrinter, "Print settings were initialized from printer");
+ ok(!settings.isInitializedFromPrefs);
+ }
+ } catch (e) {
+ ok(false, `Shouldn't throw: ${e}`);
+ console.error(e);
+ }
+ SimpleTest.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_printpreview.xhtml b/layout/base/tests/chrome/test_printpreview.xhtml
new file mode 100644
index 0000000000..c63d7a62d1
--- /dev/null
+++ b/layout/base/tests/chrome/test_printpreview.xhtml
@@ -0,0 +1,16 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<body xmlns="http://www.w3.org/1999/xhtml">
+</body>
+ <!-- test code goes here -->
+<script type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(3);
+window.openDialog("printpreview_helper.xhtml", "printpreview", "chrome,width=100,height=100,noopener", window);
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_printpreview_bug396024.xhtml b/layout/base/tests/chrome/test_printpreview_bug396024.xhtml
new file mode 100644
index 0000000000..4b839f3b2b
--- /dev/null
+++ b/layout/base/tests/chrome/test_printpreview_bug396024.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=396024
+-->
+<window title="Mozilla Bug 369024"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=396024"
+ target="_blank">Mozilla Bug 396024</a>
+</body>
+ <!-- test code goes here -->
+<script type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+window.openDialog("printpreview_bug396024_helper.xhtml", "bug396024", "chrome,width=100,height=100,noopener", window);
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_printpreview_bug482976.xhtml b/layout/base/tests/chrome/test_printpreview_bug482976.xhtml
new file mode 100644
index 0000000000..8dd4c65337
--- /dev/null
+++ b/layout/base/tests/chrome/test_printpreview_bug482976.xhtml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+ type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=482976
+-->
+<window title="Mozilla Bug 482976"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=482976"
+ target="_blank">Mozilla Bug 482976</a>
+</body>
+ <!-- test code goes here -->
+<script type="application/javascript">
+<![CDATA[
+SimpleTest.waitForExplicitFinish();
+window.openDialog("printpreview_bug482976_helper.xhtml", "bug482976", "chrome,width=100,height=100,noopener", window);
+]]></script>
+</window>
diff --git a/layout/base/tests/chrome/test_scrolling_repaints.html b/layout/base/tests/chrome/test_scrolling_repaints.html
new file mode 100644
index 0000000000..605e598c52
--- /dev/null
+++ b/layout/base/tests/chrome/test_scrolling_repaints.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that we don't get unnecessary repaints due to subpixel shifts</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+</head>
+<!-- Need a timeout here to allow paint unsuppression before we start the test -->
+<body onload="setTimeout(startTest,0)">
+<div id="t" style="width:400px; height:100px; background:yellow; overflow:hidden">
+ <div style="height:40px;"></div>
+ <div id="e" style="height:30px; background:lime"></div>
+ <div style="height:60.4px; background:pink"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var t = document.getElementById("t");
+var e = document.getElementById("e");
+var utils = window.windowUtils;
+
+function startTest() {
+ // Do a scroll to ensure we trigger activity heuristics.
+ waitForAllPaintsFlushed(function () {
+ t.scrollTop = 5;
+ // Scroll down as far as we can, to put our rendering layer at a subpixel offset within the layer
+ waitForAllPaintsFlushed(function () {
+ t.scrollTop = 1000;
+ waitForAllPaintsFlushed(function () {
+ // Clear paint state now and scroll again.
+ utils.checkAndClearPaintedState(e);
+ // scroll up a little bit. This should not cause anything to be repainted.
+ t.scrollTop = t.scrollTop - 10;
+ waitForAllPaintsFlushed(function () {
+ var painted = utils.checkAndClearPaintedState(e);
+ is(painted, false, "Fully-visible scrolled element should not have been painted");
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/chrome/test_shadow_root_adopted_styles.html b/layout/base/tests/chrome/test_shadow_root_adopted_styles.html
new file mode 100644
index 0000000000..d6701f3089
--- /dev/null
+++ b/layout/base/tests/chrome/test_shadow_root_adopted_styles.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset="utf-8">
+<body>
+<script>
+ document.body.attachShadow({mode: "open"}).innerHTML = `
+ <div class="target"></div>
+ `;
+ const sheet = new CSSStyleSheet();
+ document.body.shadowRoot.adoptedStyleSheets = [sheet];
+ sheet.replaceSync(".target { width: 100px; height: 100px; border-style: solid; border-color: blue; }");
+</script>
diff --git a/layout/base/tests/chrome/test_shadow_root_adopted_styles_ref.html b/layout/base/tests/chrome/test_shadow_root_adopted_styles_ref.html
new file mode 100644
index 0000000000..fae4a54f21
--- /dev/null
+++ b/layout/base/tests/chrome/test_shadow_root_adopted_styles_ref.html
@@ -0,0 +1,11 @@
+<!doctype html>
+<meta charset="utf-8">
+<body>
+<script>
+ document.body.attachShadow({mode: "open"}).innerHTML = `
+ <div class="target"></div>
+ <style>
+ .target { width: 100px; height: 100px; border-style: solid; border-color: blue; }
+ </style>
+ `;
+</script>
diff --git a/layout/base/tests/chrome/test_shared_adopted_styles.html b/layout/base/tests/chrome/test_shared_adopted_styles.html
new file mode 100644
index 0000000000..f5b232bce6
--- /dev/null
+++ b/layout/base/tests/chrome/test_shared_adopted_styles.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<meta charset="utf-8">
+<body>
+ <div class="target"></div>
+ <span id="shadowHostA"></span>
+ <span id="shadowHostB"></span>
+</body>
+<script>
+ const sheet = new CSSStyleSheet();
+ sheet.replaceSync(".target { width: 100px; height: 100px; border-style: solid; border-color: blue; }");
+
+ const innerHTMLText = `<div class="target"></div>`
+ shadowHostA.attachShadow({mode: "open"}).innerHTML = innerHTMLText;
+ shadowHostB.attachShadow({mode: "open"}).innerHTML = innerHTMLText;
+
+ document.adoptedStyleSheets = [sheet];
+ shadowHostA.shadowRoot.adoptedStyleSheets = [sheet];
+ shadowHostB.shadowRoot.adoptedStyleSheets = [sheet];
+</script>
diff --git a/layout/base/tests/chrome/test_shared_adopted_styles_ref.html b/layout/base/tests/chrome/test_shared_adopted_styles_ref.html
new file mode 100644
index 0000000000..b12cb5fd99
--- /dev/null
+++ b/layout/base/tests/chrome/test_shared_adopted_styles_ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<meta charset="utf-8">
+<body>
+ <div class="target"></div>
+ <span id="shadowHostA"></span>
+ <span id="shadowHostB"></span>
+ <style> .target { width: 100px; height: 100px; border-style: solid; border-color: blue; } </style>
+</body>
+<script>
+ const innerHTMLText = `
+ <div class="target"></div>
+ <style> .target { width: 100px; height: 100px; border-style: solid; border-color: blue; } </style>
+ `;
+ shadowHostA.attachShadow({mode: "open"}).innerHTML = innerHTMLText;
+ shadowHostB.attachShadow({mode: "open"}).innerHTML = innerHTMLText;
+</script>
diff --git a/layout/base/tests/chrome/test_will_change.html b/layout/base/tests/chrome/test_will_change.html
new file mode 100644
index 0000000000..fd34dc50f6
--- /dev/null
+++ b/layout/base/tests/chrome/test_will_change.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Tests for MozAfterPaint</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/tests/SimpleTest/paint_listener.js"></script>
+ <style>
+ div {
+ width: 100px;
+ height: 100px;
+ background: radial-gradient(ellipse at center, #87e0fd 0%,#53cbf1 40%,#05abe0 100%);
+ }
+ </style>
+</head>
+<body>
+</body>
+<script>
+
+var utils = window.windowUtils;
+
+function waitForPaints() {
+ return new Promise(function(resolve, reject) {
+ waitForAllPaintsFlushed(resolve);
+ });
+}
+
+add_task(async () => {
+ var element = document.createElement("div");
+ document.body.appendChild(element);
+
+ await waitForPaints();
+
+ utils.checkAndClearPaintedState(element);
+ element.style.opacity = "0.5";
+
+ await waitForPaints();
+
+ var painted = utils.checkAndClearPaintedState(element);
+ // *** We check that this repaints because the test is relying
+ // on this property. If this is broken then this test wont
+ // be reliable check for will-change.
+ is(painted, true, "element should have been painted");
+
+ element.remove();
+});
+
+add_task(async () => {
+ var element = document.createElement("div");
+ document.body.appendChild(element);
+
+ element.style.willChange = "opacity";
+
+ await waitForPaints();
+
+ utils.checkAndClearPaintedState(element);
+ element.style.opacity = "0.5";
+
+ await waitForPaints();
+
+ var painted = utils.checkAndClearPaintedState(element);
+ // BasicLayers' heuristics are so that even with will-change:opacity,
+ // we can still have repaints.
+ if (utils.layerManagerType != "Basic") {
+ is(painted, false, "will-change:opacity element should not have been painted");
+ }
+
+ element.remove();
+});
+
+add_task(async () => {
+ var element = document.createElement("div");
+ document.body.appendChild(element);
+
+ element.style.willChange = "transform";
+
+ await waitForPaints();
+
+ utils.checkAndClearPaintedState(element);
+ element.style.transform = "translateY(-5px)";
+
+ await waitForPaints();
+
+ var painted = utils.checkAndClearPaintedState(element);
+ // BasicLayers' heuristics are so that even with will-change:transform,
+ // we can still have repaints.
+ if (utils.layerManagerType != "Basic") {
+ is(painted, false, "will-change:transform element should not have been painted");
+ }
+
+ element.remove();
+});
+
+add_task(async () => {
+ var element = document.createElement("div");
+ document.body.appendChild(element);
+
+ element.style.willChange = "translate";
+
+ await waitForPaints();
+
+ utils.checkAndClearPaintedState(element);
+ element.style.translate = "5px";
+
+ await waitForPaints();
+
+ var painted = utils.checkAndClearPaintedState(element);
+ // BasicLayers' heuristics are so that even with will-change:translate,
+ // we can still have repaints.
+ if (utils.layerManagerType != "Basic") {
+ is(painted, false, "will-change:translate element should not have been painted");
+ }
+
+ element.remove();
+});
+
+add_task(async () => {
+ var element = document.createElement("div");
+ document.body.appendChild(element);
+
+ element.style.willChange = "offset-path";
+
+ await waitForPaints();
+
+ utils.checkAndClearPaintedState(element);
+ element.style.offsetPath = "path('M55 50 h1')";
+
+ await waitForPaints();
+
+ var painted = utils.checkAndClearPaintedState(element);
+ // BasicLayers' heuristics are so that even with will-change:offset-path,
+ // we can still have repaints.
+ if (utils.layerManagerType != "Basic") {
+ is(painted, false, "will-change:offset-path element should not have been painted");
+ }
+
+ element.remove();
+});
+
+</script>
+</html>
diff --git a/layout/base/tests/chrome/window_css_visibility_propagation-1.xhtml b/layout/base/tests/chrome/window_css_visibility_propagation-1.xhtml
new file mode 100644
index 0000000000..ac9c63ec14
--- /dev/null
+++ b/layout/base/tests/chrome/window_css_visibility_propagation-1.xhtml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<div id="parent" style="visibility:hidden">
+ <browser type="content" src="about:mozilla"></browser>
+</div>
+</window>
diff --git a/layout/base/tests/chrome/window_css_visibility_propagation-2.xhtml b/layout/base/tests/chrome/window_css_visibility_propagation-2.xhtml
new file mode 100644
index 0000000000..9b9e42c2d1
--- /dev/null
+++ b/layout/base/tests/chrome/window_css_visibility_propagation-2.xhtml
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<div id="parent">
+ <browser type="content" src="frame_css_visibility_propagation.html"></browser>
+</div>
+</window>
diff --git a/layout/base/tests/chrome/window_css_visibility_propagation-3.html b/layout/base/tests/chrome/window_css_visibility_propagation-3.html
new file mode 100644
index 0000000000..91a2230ee1
--- /dev/null
+++ b/layout/base/tests/chrome/window_css_visibility_propagation-3.html
@@ -0,0 +1,3 @@
+<div id="parent">
+ <iframe onload="opener.postMessage('ready');" src="window_css_visibility_propagation-2.xhtml"/>
+</div>
diff --git a/layout/base/tests/chrome/window_css_visibility_propagation-4.html b/layout/base/tests/chrome/window_css_visibility_propagation-4.html
new file mode 100644
index 0000000000..98de74059c
--- /dev/null
+++ b/layout/base/tests/chrome/window_css_visibility_propagation-4.html
@@ -0,0 +1,3 @@
+<div id="parent" style="visibility:hidden">
+ <iframe onload="opener.postMessage('ready');" src="frame_css_visibility_propagation.html"/>
+</div>
diff --git a/layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input-ref.html b/layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input-ref.html
new file mode 100644
index 0000000000..3d56d9c500
--- /dev/null
+++ b/layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input-ref.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Move Selection into an editing host before TextEditor gets blur event</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div[contenteditable] {
+ outline: none;
+ }
+ input {
+ border: none;
+ outline: none;
+ }
+</style>
+<script>
+SimpleTest.waitForFocus(() => {
+ const editingHost = document.querySelector("div[contenteditable]");
+ editingHost.addEventListener("focus", () => {
+ requestAnimationFrame(
+ () => document.documentElement.removeAttribute("class")
+ );
+ }, { once: true });
+ getSelection().collapse(editingHost, 0);
+});
+</script>
+<input>
+<div contenteditable="true" spellcheck="false"><br></div>
diff --git a/layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input.html b/layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input.html
new file mode 100644
index 0000000000..bed3636684
--- /dev/null
+++ b/layout/base/tests/collapse-selection-into-editing-host-during-blur-of-input.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Move Selection into an editing host before TextEditor gets blur event</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div[contenteditable] {
+ outline: none;
+ }
+ input {
+ border: none;
+ outline: none;
+ }
+</style>
+<script>
+SimpleTest.waitForFocus(() => {
+ const input = document.querySelector("input");
+ input.focus();
+ input.addEventListener("blur", () => {
+ const editingHost = document.querySelector("div[contenteditable]");
+ editingHost.addEventListener("focus", () => {
+ requestAnimationFrame(
+ () => document.documentElement.removeAttribute("class")
+ );
+ }, { once: true });
+ getSelection().collapse(editingHost, 0);
+
+ }, { once: true });
+ requestAnimationFrame(() => input.blur());
+});
+</script>
+<input>
+<div contenteditable="true" spellcheck="false"><br></div>
diff --git a/layout/base/tests/file_bug607529-1.html b/layout/base/tests/file_bug607529-1.html
new file mode 100644
index 0000000000..506b7092c5
--- /dev/null
+++ b/layout/base/tests/file_bug607529-1.html
@@ -0,0 +1,12 @@
+<script>
+ var bc_1 = new BroadcastChannel("bug607529_1");
+ bc_1.onmessage = (msgEvent) => {
+ if (msgEvent.data == "navigateBack") {
+ bc_1.close();
+ history.back();
+ }
+ }
+ window.onload = function() {
+ bc_1.postMessage('goback');
+ }
+</script>
diff --git a/layout/base/tests/file_bug607529.html b/layout/base/tests/file_bug607529.html
new file mode 100644
index 0000000000..bebcd09de9
--- /dev/null
+++ b/layout/base/tests/file_bug607529.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<script>
+ var bc = new BroadcastChannel("bug607529");
+ var closed = false;
+ window.onerror = function(msg, url, line) {
+ var myMsg = JSON.stringify({msg: msg, url: url, line: line, error: true});
+ bc.postMessage(myMsg);
+ }
+
+ var report = false;
+
+ function g() {
+ if (report && !closed) {
+ bc.postMessage("callbackHappened");
+ }
+ window.requestAnimationFrame(g);
+ }
+ g();
+
+ bc.onmessage = function (e) {
+ var msg = e.data;
+ if (msg == "report") {
+ report = true;
+ } else if (msg == "navigateToPage") {
+ window.location = "file_bug607529-1.html";
+ } else if (msg == "close") {
+ bc.postMessage("closed");
+ bc.close();
+ closed = true;
+ window.close();
+ }
+ };
+
+ window.onload = function() {
+ bc.postMessage("loaded");
+ }
+
+ addEventListener("pagehide", function f(e) {
+ if (!e.persisted && !report) {
+ bc.postMessage("notcached");
+ }
+ }, false);
+
+ addEventListener("pageshow", function f(e) {
+ if (e.persisted) {
+ bc.postMessage("revived");
+ }
+ }, false);
+
+
+</script>
diff --git a/layout/base/tests/file_bug839103.html b/layout/base/tests/file_bug839103.html
new file mode 100644
index 0000000000..3639d4bda3
--- /dev/null
+++ b/layout/base/tests/file_bug839103.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Document for Bug 839103</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style></style>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/base/tests/file_bug842853-frame.html b/layout/base/tests/file_bug842853-frame.html
new file mode 100644
index 0000000000..8da930232c
--- /dev/null
+++ b/layout/base/tests/file_bug842853-frame.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<a href="#anchor">Click to scroll to anchor</a><div style="height:5000px"></div><a name="anchor">FAIL</a>
+</body>
+</html>
diff --git a/layout/base/tests/file_bug842853.html b/layout/base/tests/file_bug842853.html
new file mode 100644
index 0000000000..5f216f8ade
--- /dev/null
+++ b/layout/base/tests/file_bug842853.html
@@ -0,0 +1,17 @@
+<!DOCTYPE HTML>
+<html><head>
+ <meta charset="utf-8">
+ <title>Testcase for bug </title>
+<link rel="stylesheet" href="file_bug842853.sjs">
+</head>
+<body>
+
+<a href="#anchor">Click to scroll to anchor</a><div style="height:5000px"></div><a name="anchor">FAIL</a>
+<script>
+window.addEventListener("load", () => {
+ window.parent.runTest();
+});
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/file_bug842853.sjs b/layout/base/tests/file_bug842853.sjs
new file mode 100644
index 0000000000..0ae70f7df5
--- /dev/null
+++ b/layout/base/tests/file_bug842853.sjs
@@ -0,0 +1,16 @@
+var timer;
+
+function handleRequest(request, response) {
+ response.setHeader("Cache-Control", "no-cache, must-revalidate", false);
+ response.setHeader("Content-Type", "text/css", false);
+ response.write("body { background:lime; color:red; }");
+ response.processAsync();
+ timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+ timer.initWithCallback(
+ function () {
+ response.finish();
+ },
+ 500,
+ Ci.nsITimer.TYPE_ONE_SHOT
+ );
+}
diff --git a/layout/base/tests/file_dynamic_toolbar_max_height.html b/layout/base/tests/file_dynamic_toolbar_max_height.html
new file mode 100644
index 0000000000..0b99ef496c
--- /dev/null
+++ b/layout/base/tests/file_dynamic_toolbar_max_height.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+<title>Tests metrics with dynamic toolbar</title>
+<script>
+const ok = opener.ok.bind(opener);
+const is = opener.is.bind(opener);
+const original_finish = opener.SimpleTest.finish;
+const SimpleTest = opener.SimpleTest;
+const add_task = opener.add_task;
+SimpleTest.finish = function finish() {
+ self.close();
+ original_finish();
+}
+</script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+<style>
+html {
+ scrollbar-width: none;
+}
+#percent {
+ position: absolute;
+ height: 100%;
+}
+#vh {
+ position: absolute;
+ height: 100vh;
+}
+</style>
+<div id="percent"></div>
+<div id="vh"></div>
+<script>
+'use strict';
+
+SpecialPowers.DOMWindowUtils.setDynamicToolbarMaxHeight(0);
+
+let percentHeight = getComputedStyle(percent).height;
+let vhHeight = getComputedStyle(vh).height;
+is(percentHeight, vhHeight,
+ "%-units and vh-units should be the same when the dynamic toolbar max " +
+ "height is zero");
+
+SpecialPowers.DOMWindowUtils.setDynamicToolbarMaxHeight(50);
+
+percentHeight = getComputedStyle(percent).height;
+vhHeight = getComputedStyle(vh).height;
+is(parseInt(percentHeight) + 50, parseInt(vhHeight),
+ "vh units should be 50px greater than %-units");
+is(document.documentElement.clientHeight, parseInt(percentHeight),
+ "documentElement.clientHeight should equal to %-units");
+ok(matchMedia(`(height: ${percentHeight})`).matches,
+ "Media Queries' height is not including the dynamic toolbar max height");
+
+SimpleTest.finish();
+
+</script>
diff --git a/layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame1.html b/layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame1.html
new file mode 100644
index 0000000000..f072b65117
--- /dev/null
+++ b/layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame1.html
@@ -0,0 +1,3 @@
+<!DOCTYPE HTML>
+<html style='padding:25px'>
+<div id='f1d' style='position:absolute; left:14px; top:15px; width:16px; height:17px; background:pink'></div>
diff --git a/layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame2.html b/layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame2.html
new file mode 100644
index 0000000000..a2ebad8183
--- /dev/null
+++ b/layout/base/tests/file_getBoxQuads_convertPointRectQuad_frame2.html
@@ -0,0 +1 @@
+<div id='d'>
diff --git a/layout/base/tests/file_lazyload_telemetry.html b/layout/base/tests/file_lazyload_telemetry.html
new file mode 100644
index 0000000000..473822c37f
--- /dev/null
+++ b/layout/base/tests/file_lazyload_telemetry.html
@@ -0,0 +1,9 @@
+<!DOCTYPE HTML>
+<html>
+ <body>
+ <img id="image1" src="image_rgrg-256x256.png" loading="lazy" style="display:block; width:100px; height:50vh; margin-bottom: 1px;">
+ <img id="image2" src="image_rgrg-256x256.png" loading="lazy" style="display:block; width:100px; height:50vh; margin-bottom: 1px;">
+ <img id="image3" src="image_rgrg-256x256.png" loading="lazy" style="display:block; width:100px; height:50vh; margin-bottom: 1px;">
+ <img id="image4" src="image_rgrg-256x256.png" loading="lazy" style="display:block; width:100px; height:50vh; margin-bottom: 1px;">
+ </body>
+</html>
diff --git a/layout/base/tests/file_synthmousemove.html b/layout/base/tests/file_synthmousemove.html
new file mode 100644
index 0000000000..74d327dbe9
--- /dev/null
+++ b/layout/base/tests/file_synthmousemove.html
@@ -0,0 +1,49 @@
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ .spacer {
+ height: 50px;
+ }
+ .special {
+ height: 200px;
+ background-color: blue;
+ }
+ .special:hover {
+ background-color: red;
+ }
+</style>
+<div id="x" class="spacer"></div>
+<div id="x2" class="special"></div>
+<div style="margin-top: 100vh">
+ <!-- this is here so it wrongly gets sent the events -->
+ <iframe src="https://example.com/tests/layout/base/tests/helper_synthmousemove.html"></iframe>
+</div>
+<div style="height: 300vh"></div>
+<script>
+
+async function runTest() {
+ let thex = document.getElementById("x");
+ let thex2 = document.getElementById("x2")
+ synthesizeMouse(thex, 20, 20, {type: "mousemove"});
+ opener.is(
+ getComputedStyle(thex2).backgroundColor,
+ "rgb(0, 0, 255)",
+ "Part is blue"
+ );
+
+ thex.remove();
+ document.documentElement.getBoundingClientRect();
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+ opener.is(
+ getComputedStyle(thex2).backgroundColor,
+ "rgb(255, 0, 0)",
+ "Part is red"
+ );
+
+ opener.reportSuccess();
+ window.close();
+}
+
+SimpleTest.waitForFocus(runTest);
+</script>
diff --git a/layout/base/tests/file_zoom_restore_bfcache.html b/layout/base/tests/file_zoom_restore_bfcache.html
new file mode 100644
index 0000000000..77451f3ef6
--- /dev/null
+++ b/layout/base/tests/file_zoom_restore_bfcache.html
@@ -0,0 +1,92 @@
+<!doctype html>
+<script>
+ var bcName = "zoomRestoreBfcache" + window.location.search;
+ var bc = new BroadcastChannel(bcName);
+ if (window.location.search == "?2") {
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ dump(`Subpage ?2 received command=${command}\n`);
+ switch (command) {
+ case "case2sendData": {
+ bc.postMessage({command: "case2data", devicePixelRatio: window.devicePixelRatio,
+ frameDevicePixelRatio: document.querySelector("iframe").contentWindow.devicePixelRatio});
+ break;
+ }
+ case "case2action": {
+ SpecialPowers.spawnChrome([], () => {
+ const FullZoom = this.browsingContext.embedderElement.ownerGlobal.FullZoom;
+ FullZoom.setZoom(2.0);
+ });
+ SpecialPowers.setFullZoom(window, 2);
+ window.requestAnimationFrame(() => window.requestAnimationFrame(() => {
+ bc.postMessage({command: "case2dataAnimationFrame", devicePixelRatio: window.devicePixelRatio,
+ frameDevicePixelRatio: document.querySelector("iframe").contentWindow.devicePixelRatio });
+ }));
+ break;
+ }
+ case "case2back": {
+ bc.close();
+ window.history.back();
+ break;
+ }
+ }
+ }
+ } else {
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ dump(`Subpage received command=${command}\n`);
+ switch (command) {
+ case "case1sendData": {
+ bc.postMessage({command: "case1data", devicePixelRatio: window.devicePixelRatio});
+ break;
+ }
+ case "case1click": {
+ document.querySelector("a").click();
+ // We are opening file_zoom_restore_bfcache.html?2, so the current
+ // page is going into bfcache
+ break;
+ }
+ case "case3sendData": {
+ // We came back from bfcache
+ SpecialPowers.spawnChrome([], () => {
+ // We use FullZoom to set the zoom level in the parent, but if FullZoom is not
+ // available then that will fail. So we don't wait here for the devicePixelRatio
+ // to change if FullZoom is not available, and test_zoom_restore_bfcache.html
+ // will mark this test as todo.
+ return "FullZoom" in this.browsingContext.embedderElement.ownerGlobal;
+ }).then((hasFullZoom) => {
+ function waitUntilZoomLevelRestored() {
+ // Zoom level is updated asynchronously when bfcache lives in the
+ // parent process.
+ if (!hasFullZoom || window.devicePixelRatio == 2) {
+ bc.postMessage({command: "case3data", devicePixelRatio: window.devicePixelRatio,
+ frameDevicePixelRatio: document.querySelector("iframe").contentWindow.devicePixelRatio});
+ return;
+ }
+ window.requestAnimationFrame(waitUntilZoomLevelRestored);
+ }
+ window.requestAnimationFrame(waitUntilZoomLevelRestored);
+ });
+ break;
+ }
+ case "close": {
+ SpecialPowers.spawnChrome([], () => {
+ const FullZoom = this.browsingContext.embedderElement.ownerGlobal.FullZoom;
+ FullZoom.setZoom(1.0);
+ });
+ bc.postMessage({command: "closed"});
+ bc.close();
+ window.close();
+ break;
+ }
+ }
+ }
+ }
+ window.addEventListener("pageshow", function(e) {
+ bc.postMessage({command: "handlePageShow", eventPersisted: e.persisted});
+ });
+</script>
+<a href="?2">This is a very interesting page</a>
+<iframe srcdoc="And this is a nested frame"></iframe>
diff --git a/layout/base/tests/helper_bug1701027-1.html b/layout/base/tests/helper_bug1701027-1.html
new file mode 100644
index 0000000000..659c1f7826
--- /dev/null
+++ b/layout/base/tests/helper_bug1701027-1.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+Here is some text to stare at as the test runs. It serves no functional
+purpose
+<img src="notoifnd" style="position: absolute;">
+<div id="fd" style="position: fixed; left:0; top:0;bottom:0;right:0;"></div>
+</body>
+</html>
diff --git a/layout/base/tests/helper_bug1701027-2.html b/layout/base/tests/helper_bug1701027-2.html
new file mode 100644
index 0000000000..659c1f7826
--- /dev/null
+++ b/layout/base/tests/helper_bug1701027-2.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+</head>
+<body>
+Here is some text to stare at as the test runs. It serves no functional
+purpose
+<img src="notoifnd" style="position: absolute;">
+<div id="fd" style="position: fixed; left:0; top:0;bottom:0;right:0;"></div>
+</body>
+</html>
diff --git a/layout/base/tests/helper_synthmousemove.html b/layout/base/tests/helper_synthmousemove.html
new file mode 100644
index 0000000000..41d8c6525a
--- /dev/null
+++ b/layout/base/tests/helper_synthmousemove.html
@@ -0,0 +1,3 @@
+<!doctype html>
+<title>helper_synthmousemove.html</title>
+<div></div>
diff --git a/layout/base/tests/image_rgrg-256x256.png b/layout/base/tests/image_rgrg-256x256.png
new file mode 100644
index 0000000000..e6fba3daa5
--- /dev/null
+++ b/layout/base/tests/image_rgrg-256x256.png
Binary files differ
diff --git a/layout/base/tests/input-invalid-ref.html b/layout/base/tests/input-invalid-ref.html
new file mode 100644
index 0000000000..4b34c9a2f3
--- /dev/null
+++ b/layout/base/tests/input-invalid-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <input value="foo" style="background-color:red">
+ </body>
+</html>
+
diff --git a/layout/base/tests/input-maxlength-invalid-change.html b/layout/base/tests/input-maxlength-invalid-change.html
new file mode 100644
index 0000000000..849445f85f
--- /dev/null
+++ b/layout/base/tests/input-maxlength-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with maxlength is invalid if the user edits and it's too long -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ synthesizeKey("KEY_Backspace");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" maxlength="2" value="fooo">
+ </body>
+</html>
diff --git a/layout/base/tests/input-maxlength-ui-invalid-change.html b/layout/base/tests/input-maxlength-ui-invalid-change.html
new file mode 100644
index 0000000000..1f74f0730c
--- /dev/null
+++ b/layout/base/tests/input-maxlength-ui-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with maxlength is -moz-ui-invalid if the user edits and it's too long -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ synthesizeKey("KEY_Backspace");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" maxlength="2" value="fooo">
+ </body>
+</html>
diff --git a/layout/base/tests/input-maxlength-ui-valid-change.html b/layout/base/tests/input-maxlength-ui-valid-change.html
new file mode 100644
index 0000000000..47224772fa
--- /dev/null
+++ b/layout/base/tests/input-maxlength-ui-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with maxlength is -moz-ui-valid if the user edits and it's not too long -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ synthesizeKey("KEY_Backspace"); // so that it becomes invalid first
+ input.blur();
+ input.focus();
+ synthesizeKey("KEY_Backspace");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" maxlength="3" value="foooo">
+ </body>
+</html>
diff --git a/layout/base/tests/input-maxlength-valid-before-change.html b/layout/base/tests/input-maxlength-valid-before-change.html
new file mode 100644
index 0000000000..8662e8f5f4
--- /dev/null
+++ b/layout/base/tests/input-maxlength-valid-before-change.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with maxlength is valid until the user edits it, even if it's too long -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ </head>
+ <body onload="document.documentElement.className=''">
+ <input id="input" maxlength="2" value="foo">
+ </body>
+</html>
+
diff --git a/layout/base/tests/input-maxlength-valid-change.html b/layout/base/tests/input-maxlength-valid-change.html
new file mode 100644
index 0000000000..2612642534
--- /dev/null
+++ b/layout/base/tests/input-maxlength-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with maxlength is valid if the user edits and it's not too long -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ synthesizeKey("KEY_Backspace"); // so that it becomes invalid first
+ input.blur();
+ input.focus();
+ synthesizeKey("KEY_Backspace");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" maxlength="3" value="foooo">
+ </body>
+</html>
diff --git a/layout/base/tests/input-minlength-invalid-change.html b/layout/base/tests/input-minlength-invalid-change.html
new file mode 100644
index 0000000000..543c3e335d
--- /dev/null
+++ b/layout/base/tests/input-minlength-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with minlength is invalid if the user edits and it's too short -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ sendString("o");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" minlength="4" value="fo">
+ </body>
+</html>
diff --git a/layout/base/tests/input-minlength-ui-invalid-change.html b/layout/base/tests/input-minlength-ui-invalid-change.html
new file mode 100644
index 0000000000..6c5dfc0e22
--- /dev/null
+++ b/layout/base/tests/input-minlength-ui-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with minlength is -moz-ui-invalid if the user edits and it's too short -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ sendString("o");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" minlength="4" value="fo">
+ </body>
+</html>
diff --git a/layout/base/tests/input-minlength-ui-valid-change.html b/layout/base/tests/input-minlength-ui-valid-change.html
new file mode 100644
index 0000000000..96e6390b91
--- /dev/null
+++ b/layout/base/tests/input-minlength-ui-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with minlength is -moz-ui-valid if the user edits and it's not too short -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ sendString("o"); // so that it becomes invalid first
+ input.blur();
+ input.focus();
+ sendString("o");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" minlength="3" value="f">
+ </body>
+</html>
diff --git a/layout/base/tests/input-minlength-valid-before-change.html b/layout/base/tests/input-minlength-valid-before-change.html
new file mode 100644
index 0000000000..21e6927926
--- /dev/null
+++ b/layout/base/tests/input-minlength-valid-before-change.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with minlength is valid until the user edits it, even if it's too short -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ </head>
+ <body onload="document.documentElement.className=''">
+ <input id="input" minlength="5" value="foo">
+ </body>
+</html>
+
diff --git a/layout/base/tests/input-minlength-valid-change.html b/layout/base/tests/input-minlength-valid-change.html
new file mode 100644
index 0000000000..92b7fd3390
--- /dev/null
+++ b/layout/base/tests/input-minlength-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: input with minlength is valid if the user edits and it's not too short -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var input = document.getElementById('input');
+ input.setSelectionRange(input.value.length, input.value.length)
+ input.focus();
+ sendString("o"); // so that it becomes invalid first
+ input.blur();
+ input.focus();
+ sendString("o");
+ input.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <input id="input" minlength="3" value="f">
+ </body>
+</html>
diff --git a/layout/base/tests/input-password-RTL-input-ref.html b/layout/base/tests/input-password-RTL-input-ref.html
new file mode 100644
index 0000000000..f3b5efe3ae
--- /dev/null
+++ b/layout/base/tests/input-password-RTL-input-ref.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<input type="password">
+<script>
+ function runTest() {
+ let hash = window.location.hash;
+ let input = document.getElementsByTagName("input")[0];
+ input.focus();
+ synthesizeKey("a");
+ synthesizeKey("b");
+ synthesizeKey("c");
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/input-password-RTL-input.html b/layout/base/tests/input-password-RTL-input.html
new file mode 100644
index 0000000000..3c73d3ef1c
--- /dev/null
+++ b/layout/base/tests/input-password-RTL-input.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+<input type="password">
+<script>
+ function runTest() {
+ let hash = window.location.hash;
+ let input = document.getElementsByTagName("input")[0];
+ input.focus();
+ synthesizeKey("a");
+ synthesizeKey("b");
+ switch (hash) {
+ case "#arabic":
+ synthesizeKey("\u0634");
+ break;
+ case "#hebrew":
+ synthesizeKey("\u05D3");
+ break;
+ }
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/input-password-remask-ref.html b/layout/base/tests/input-password-remask-ref.html
new file mode 100644
index 0000000000..a7f105a01e
--- /dev/null
+++ b/layout/base/tests/input-password-remask-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<input type="password" value="123456">
+<script>
+ function runTest() {
+ let input = document.getElementsByTagName("input")[0];
+ input.focus();
+ input.setSelectionRange(3, 4);
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/input-password-remask.html b/layout/base/tests/input-password-remask.html
new file mode 100644
index 0000000000..31149eac8d
--- /dev/null
+++ b/layout/base/tests/input-password-remask.html
@@ -0,0 +1,23 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<input type="password" value="abcdef">
+<script>
+ function runTest() {
+ let input = document.getElementsByTagName("input")[0];
+ input.focus();
+ input.setSelectionRange(3, 4);
+ let editor = SpecialPowers.wrap(input).editor;
+ editor.unmask(0, 6);
+ editor.mask();
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/input-password-unmask-around-emoji-ref.html b/layout/base/tests/input-password-unmask-around-emoji-ref.html
new file mode 100644
index 0000000000..1fba0d9ae1
--- /dev/null
+++ b/layout/base/tests/input-password-unmask-around-emoji-ref.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<input type="text">
+<script>
+ function runTest() {
+ let params = window.location.hash.substring(1).split("-");
+ let input = document.getElementsByTagName("input")[0];
+ let editor = SpecialPowers.wrap(input).editor;
+ let unmaskStart = Number.parseInt(params[0]);
+ let unmaskEnd = Number.parseInt(params[1]);
+ let value = "";
+ if (unmaskStart == 0 && unmaskEnd > 0) {
+ value += "a";
+ } else {
+ value += editor.passwordMask;
+ }
+ if (unmaskStart <= 2 && unmaskEnd > 1 && unmaskEnd > unmaskStart) {
+ value += String.fromCodePoint(0x1f914);
+ } else {
+ value += editor.passwordMask;
+ value += editor.passwordMask;
+ }
+ if (unmaskStart <= 3 && unmaskEnd > 3) {
+ value += "b";
+ } else {
+ value += editor.passwordMask;
+ }
+ input.value = value;
+ input.setSelectionRange(Number.parseInt(params[2]), Number.parseInt(params[3]));
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/input-password-unmask-around-emoji.html b/layout/base/tests/input-password-unmask-around-emoji.html
new file mode 100644
index 0000000000..97df884850
--- /dev/null
+++ b/layout/base/tests/input-password-unmask-around-emoji.html
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<input type="password" value="a&#x1f914;b">
+<script>
+ function runTest() {
+ let params = window.location.hash.substring(1).split("-");
+ let input = document.getElementsByTagName("input")[0];
+ let editor = SpecialPowers.wrap(input).editor;
+ editor.unmask(Number.parseInt(params[0]), Number.parseInt(params[1]));
+ input.setSelectionRange(Number.parseInt(params[2]), Number.parseInt(params[3]));
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/input-password-unmask-ref.html b/layout/base/tests/input-password-unmask-ref.html
new file mode 100644
index 0000000000..d1803551e4
--- /dev/null
+++ b/layout/base/tests/input-password-unmask-ref.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<input type="text">
+<script>
+ function runTest() {
+ let params = window.location.hash.split("-");
+ let input = document.getElementsByTagName("input")[0];
+ let editor = SpecialPowers.wrap(input).editor;
+ let unmaskStart = Number.parseInt(params[1]);
+ let unmaskEnd = Number.parseInt(params[2]);
+ let value = "";
+ for (let i = 0; i < params[0].length - 1; i++) {
+ let c = params[0].charAt(i + 1);
+ if (i < unmaskStart || i >= unmaskEnd) {
+ c = editor.passwordMask;
+ } else if (c === "_") {
+ c = " ";
+ }
+ value += c;
+ }
+ input.value = value;
+ input.setSelectionRange(Number.parseInt(params[3]), Number.parseInt(params[4]));
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/input-password-unmask.html b/layout/base/tests/input-password-unmask.html
new file mode 100644
index 0000000000..34c63726bb
--- /dev/null
+++ b/layout/base/tests/input-password-unmask.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait">
+<head>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+</head>
+<body>
+<input type="password">
+<script>
+ function runTest() {
+ let params = window.location.hash.split("-");
+ let input = document.getElementsByTagName("input")[0];
+ input.value = params[0].replace("_", " ").substring(1);
+ let editor = SpecialPowers.wrap(input).editor;
+ editor.unmask(Number.parseInt(params[1]), Number.parseInt(params[2]));
+ input.setSelectionRange(Number.parseInt(params[3]), Number.parseInt(params[4]));
+ if (params.length > 5) {
+ input.style.textTransform = params[5];
+ }
+ document.documentElement.removeAttribute("class");
+ }
+
+ SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/input-stoppropagation-ref.html b/layout/base/tests/input-stoppropagation-ref.html
new file mode 100644
index 0000000000..99ff791588
--- /dev/null
+++ b/layout/base/tests/input-stoppropagation-ref.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+function onLoad() {
+ document.getElementById("input1").focus();
+ synthesizeKey("KEY_Tab");
+ synthesizeKey("KEY_Tab", {shiftKey: true});
+ synthesizeKey("KEY_Tab");
+}
+</script>
+<body onload="onLoad()">
+ <input type="text" id="input1"></input>
+ <input type="text" id="input2"></input>
+</body>
+</html>
diff --git a/layout/base/tests/input-stoppropagation.html b/layout/base/tests/input-stoppropagation.html
new file mode 100644
index 0000000000..b246a6b6da
--- /dev/null
+++ b/layout/base/tests/input-stoppropagation.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+function onLoad() {
+ let input2 = document.getElementById("input2");
+ input2.addEventListener("focus", e => {
+ e.stopImmediatePropagation();
+ });
+ document.getElementById("input1").focus();
+ synthesizeKey("KEY_Tab");
+ synthesizeKey("KEY_Tab", {shiftKey: true});
+ synthesizeKey("KEY_Tab");
+}
+</script>
+<body onload="onLoad()">
+ <input type="text" id="input1"></input>
+ <input type="text" id="input2"></input>
+</body>
+</html>
diff --git a/layout/base/tests/input-ui-valid-ref.html b/layout/base/tests/input-ui-valid-ref.html
new file mode 100644
index 0000000000..76d9386678
--- /dev/null
+++ b/layout/base/tests/input-ui-valid-ref.html
@@ -0,0 +1,6 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <input value="foo" style="background-color:green">
+ </body>
+</html>
diff --git a/layout/base/tests/input-valid-ref.html b/layout/base/tests/input-valid-ref.html
new file mode 100644
index 0000000000..ec01bb98f2
--- /dev/null
+++ b/layout/base/tests/input-valid-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <input value="foo" style="background-color:green">
+ </body>
+</html>
+
diff --git a/layout/base/tests/interlinePosition-after-Selection-addRange-ref.html b/layout/base/tests/interlinePosition-after-Selection-addRange-ref.html
new file mode 100644
index 0000000000..55eccf814f
--- /dev/null
+++ b/layout/base/tests/interlinePosition-after-Selection-addRange-ref.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Selection.addRange() should always reset interline position</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div[contenteditable] {
+ padding: 1em;
+ }
+</style>
+<script>
+SimpleTest.waitForFocus(async () => {
+ const editingHost = document.querySelector("div[contenteditable]");
+ editingHost.focus();
+ getSelection().collapse(editingHost, 2);
+ SpecialPowers.wrap(getSelection()).interlinePosition = true;
+ document.documentElement.removeAttribute("class");
+});
+</script>
+<div contenteditable="true" spellcheck="false">abc<br><br></div>
diff --git a/layout/base/tests/interlinePosition-after-Selection-addRange.html b/layout/base/tests/interlinePosition-after-Selection-addRange.html
new file mode 100644
index 0000000000..8e45277249
--- /dev/null
+++ b/layout/base/tests/interlinePosition-after-Selection-addRange.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html class="reftest-wait">
+<meta charset="utf-8">
+<title>Selection.addRange() should always reset interline position</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+ div[contenteditable] {
+ padding: 1em;
+ }
+</style>
+<script>
+SimpleTest.waitForFocus(async () => {
+ const editingHost = document.querySelector("div[contenteditable]");
+ editingHost.focus();
+ getSelection().collapse(editingHost, 2);
+ SpecialPowers.wrap(getSelection()).interlinePosition = false;
+ getSelection().addRange(getSelection().getRangeAt(0));
+ document.documentElement.removeAttribute("class");
+});
+</script>
+<div contenteditable="true" spellcheck="false">abc<br><br></div>
diff --git a/layout/base/tests/marionette/manifest.ini b/layout/base/tests/marionette/manifest.ini
new file mode 100644
index 0000000000..8249f4010a
--- /dev/null
+++ b/layout/base/tests/marionette/manifest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+prefs =
+ gfx.font_loader.delay=0
+run-if = buildapp == 'browser'
+[test_accessiblecaret_cursor_mode.py]
+[test_accessiblecaret_selection_mode.py]
diff --git a/layout/base/tests/marionette/selection.py b/layout/base/tests/marionette/selection.py
new file mode 100644
index 0000000000..6cfe155927
--- /dev/null
+++ b/layout/base/tests/marionette/selection.py
@@ -0,0 +1,335 @@
+# -*- coding: utf-8 -*-
+# 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/.
+
+from marionette_driver.marionette import Actions, errors
+
+
+class CaretActions(Actions):
+ def __init__(self, marionette):
+ super(CaretActions, self).__init__(marionette)
+ self._reset_action_chain()
+
+ def _reset_action_chain(self):
+ self.mouse_chain = self.sequence(
+ "pointer", "pointer_id", {"pointerType": "mouse"}
+ )
+ self.key_chain = self.sequence("key", "keyboard_id")
+
+ def flick(self, element, x1, y1, x2, y2, duration=200):
+ """Perform a flick gesture on the target element.
+
+ :param element: The element to perform the flick gesture on.
+ :param x1: Starting x-coordinate of flick, relative to the top left
+ corner of the element.
+ :param y1: Starting y-coordinate of flick, relative to the top left
+ corner of the element.
+ :param x2: Ending x-coordinate of flick, relative to the top left
+ corner of the element.
+ :param y2: Ending y-coordinate of flick, relative to the top left
+ corner of the element.
+
+ """
+ rect = element.rect
+ el_x, el_y = rect["x"], rect["y"]
+
+ # Add element's (x, y) to make the coordinate relative to the viewport.
+ from_x, from_y = int(el_x + x1), int(el_y + y1)
+ to_x, to_y = int(el_x + x2), int(el_y + y2)
+
+ self.mouse_chain.pointer_move(from_x, from_y).pointer_down().pointer_move(
+ to_x, to_y, duration=duration
+ ).pointer_up()
+ return self
+
+ def send_keys(self, keys):
+ """Perform a keyDown and keyUp action for each character in `keys`.
+
+ :param keys: String of keys to perform key actions with.
+
+ """
+ self.key_chain.send_keys(keys)
+ return self
+
+ def perform(self):
+ """Perform the action chain built so far to the server side for execution
+ and clears the current chain of actions.
+
+ Warning: This method performs all the mouse actions before all the key
+ actions!
+
+ """
+ self.mouse_chain.perform()
+ self.key_chain.perform()
+ self._reset_action_chain()
+
+
+class SelectionManager(object):
+ """Interface for manipulating the selection and carets of the element.
+
+ We call the blinking cursor (nsCaret) as cursor, and call AccessibleCaret as
+ caret for short.
+
+ Simple usage example:
+
+ ::
+
+ element = marionette.find_element(By.ID, 'input')
+ sel = SelectionManager(element)
+ sel.move_caret_to_front()
+
+ """
+
+ def __init__(self, element):
+ self.element = element
+
+ def _input_or_textarea(self):
+ """Return True if element is either <input> or <textarea>."""
+ return self.element.tag_name in ("input", "textarea")
+
+ def js_selection_cmd(self):
+ """Return a command snippet to get selection object.
+
+ If the element is <input> or <textarea>, return the selection object
+ associated with it. Otherwise, return the current selection object.
+
+ Note: "element" must be provided as the first argument to
+ execute_script().
+
+ """
+ if self._input_or_textarea():
+ # We must unwrap sel so that DOMRect could be returned to Python
+ # side.
+ return """var sel = arguments[0].editor.selection;"""
+ else:
+ return """var sel = window.getSelection();"""
+
+ def move_cursor_by_offset(self, offset, backward=False):
+ """Move cursor in the element by character offset.
+
+ :param offset: Move the cursor to the direction by offset characters.
+ :param backward: Optional, True to move backward; Default to False to
+ move forward.
+
+ """
+ cmd = (
+ self.js_selection_cmd()
+ + """
+ for (let i = 0; i < {0}; ++i) {{
+ sel.modify("move", "{1}", "character");
+ }}
+ """.format(
+ offset, "backward" if backward else "forward"
+ )
+ )
+
+ self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox="system"
+ )
+
+ def move_cursor_to_front(self):
+ """Move cursor in the element to the front of the content."""
+ if self._input_or_textarea():
+ cmd = """arguments[0].setSelectionRange(0, 0);"""
+ else:
+ cmd = """var sel = window.getSelection();
+ sel.collapse(arguments[0].firstChild, 0);"""
+
+ self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox=None
+ )
+
+ def move_cursor_to_end(self):
+ """Move cursor in the element to the end of the content."""
+ if self._input_or_textarea():
+ cmd = """var len = arguments[0].value.length;
+ arguments[0].setSelectionRange(len, len);"""
+ else:
+ cmd = """var sel = window.getSelection();
+ sel.collapse(arguments[0].lastChild, arguments[0].lastChild.length);"""
+
+ self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox=None
+ )
+
+ def selection_rect_list(self, idx):
+ """Return the selection's DOMRectList object for the range at given idx.
+
+ If the element is either <input> or <textarea>, return the DOMRectList of
+ the range at given idx of the selection within the element. Otherwise,
+ return the DOMRectList of the of the range at given idx of current selection.
+
+ """
+ cmd = (
+ self.js_selection_cmd()
+ + """return sel.getRangeAt({}).getClientRects();""".format(idx)
+ )
+ return self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox="system"
+ )
+
+ def range_count(self):
+ """Get selection's range count"""
+ cmd = self.js_selection_cmd() + """return sel.rangeCount;"""
+ return self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox="system"
+ )
+
+ def _selection_location_helper(self, location_type):
+ """Return the start and end location of the selection in the element.
+
+ Return a tuple containing two pairs of (x, y) coordinates of the start
+ and end locations in the element. The coordinates are relative to the
+ top left-hand corner of the element. Both ltr and rtl directions are
+ considered.
+
+ """
+ range_count = self.range_count()
+ if range_count <= 0:
+ raise errors.MarionetteException(
+ "Expect at least one range object in Selection, but found nothing!"
+ )
+
+ # FIXME (Bug 1682382): We shouldn't need the retry for-loops if
+ # selection_rect_list() can reliably return a valid list.
+ retry_times = 3
+ for _ in range(retry_times):
+ try:
+ first_rect_list = self.selection_rect_list(0)
+ first_rect = first_rect_list["0"]
+ break
+ except KeyError:
+ continue
+ else:
+ raise errors.MarionetteException(
+ "Expect at least one rect in the first range, but found nothing!"
+ )
+
+ for _ in range(retry_times):
+ try:
+ # Making a selection over some non-selectable elements can
+ # create multiple ranges.
+ last_rect_list = (
+ first_rect_list
+ if range_count == 1
+ else self.selection_rect_list(range_count - 1)
+ )
+ last_list_length = last_rect_list["length"]
+ last_rect = last_rect_list[str(last_list_length - 1)]
+ break
+ except KeyError:
+ continue
+ else:
+ raise errors.MarionetteException(
+ "Expect at least one rect in the last range, but found nothing!"
+ )
+
+ origin_x, origin_y = self.element.rect["x"], self.element.rect["y"]
+
+ if self.element.get_property("dir") == "rtl": # such as Arabic
+ start_pos, end_pos = "right", "left"
+ else:
+ start_pos, end_pos = "left", "right"
+
+ # Calculate y offset according to different needs.
+ if location_type == "center":
+ start_y_offset = first_rect["height"] / 2.0
+ end_y_offset = last_rect["height"] / 2.0
+ elif location_type == "caret":
+ # Selection carets' tip are below the bottom of the two ends of the
+ # selection. Add 5px to y should be sufficient to locate them.
+ caret_tip_y_offset = 5
+ start_y_offset = first_rect["height"] + caret_tip_y_offset
+ end_y_offset = last_rect["height"] + caret_tip_y_offset
+ else:
+ start_y_offset = end_y_offset = 0
+
+ caret1_x = first_rect[start_pos] - origin_x
+ caret1_y = first_rect["top"] + start_y_offset - origin_y
+ caret2_x = last_rect[end_pos] - origin_x
+ caret2_y = last_rect["top"] + end_y_offset - origin_y
+
+ return ((caret1_x, caret1_y), (caret2_x, caret2_y))
+
+ def selection_location(self):
+ """Return the start and end location of the selection in the element.
+
+ Return a tuple containing two pairs of (x, y) coordinates of the start
+ and end of the selection. The coordinates are relative to the top
+ left-hand corner of the element. Both ltr and rtl direction are
+ considered.
+
+ """
+ return self._selection_location_helper("center")
+
+ def carets_location(self):
+ """Return a pair of the two carets' location.
+
+ Return a tuple containing two pairs of (x, y) coordinates of the two
+ carets' tip. The coordinates are relative to the top left-hand corner of
+ the element. Both ltr and rtl direction are considered.
+
+ """
+ return self._selection_location_helper("caret")
+
+ def cursor_location(self):
+ """Return the blanking cursor's center location within the element.
+
+ Return (x, y) coordinates of the cursor's center relative to the top
+ left-hand corner of the element.
+
+ """
+ return self._selection_location_helper("center")[0]
+
+ def first_caret_location(self):
+ """Return the first caret's location.
+
+ Return (x, y) coordinates of the first caret's tip relative to the top
+ left-hand corner of the element.
+
+ """
+ return self.carets_location()[0]
+
+ def second_caret_location(self):
+ """Return the second caret's location.
+
+ Return (x, y) coordinates of the second caret's tip relative to the top
+ left-hand corner of the element.
+
+ """
+ return self.carets_location()[1]
+
+ def select_all(self):
+ """Select all the content in the element."""
+ if self._input_or_textarea():
+ cmd = """var len = arguments[0].value.length;
+ arguments[0].focus();
+ arguments[0].setSelectionRange(0, len);"""
+ else:
+ cmd = """var range = document.createRange();
+ range.setStart(arguments[0].firstChild, 0);
+ range.setEnd(arguments[0].lastChild, arguments[0].lastChild.length);
+ var sel = window.getSelection();
+ sel.removeAllRanges();
+ sel.addRange(range);"""
+
+ self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox=None
+ )
+
+ @property
+ def content(self):
+ """Return all the content of the element."""
+ if self._input_or_textarea():
+ return self.element.get_property("value")
+ else:
+ return self.element.text
+
+ @property
+ def selected_content(self):
+ """Return the selected portion of the content in the element."""
+ cmd = self.js_selection_cmd() + """return sel.toString();"""
+ return self.element.marionette.execute_script(
+ cmd, script_args=(self.element,), sandbox="system"
+ )
diff --git a/layout/base/tests/marionette/test_accessiblecaret_cursor_mode.py b/layout/base/tests/marionette/test_accessiblecaret_cursor_mode.py
new file mode 100644
index 0000000000..a6c7944817
--- /dev/null
+++ b/layout/base/tests/marionette/test_accessiblecaret_cursor_mode.py
@@ -0,0 +1,285 @@
+# -*- coding: utf-8 -*-
+# 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/.
+
+import string
+import sys
+import os
+
+# Add this directory to the import path.
+sys.path.append(os.path.dirname(__file__))
+
+from selection import (
+ CaretActions,
+ SelectionManager,
+)
+from marionette_driver.by import By
+from marionette_harness.marionette_test import (
+ MarionetteTestCase,
+ parameterized,
+)
+
+
+class AccessibleCaretCursorModeTestCase(MarionetteTestCase):
+ """Test cases for AccessibleCaret under cursor mode.
+
+ We call the blinking cursor (nsCaret) as cursor, and call AccessibleCaret as
+ caret for short.
+
+ """
+
+ # Element IDs.
+ _input_id = "input"
+ _input_padding_id = "input-padding"
+ _textarea_id = "textarea"
+ _textarea_one_line_id = "textarea-one-line"
+ _contenteditable_id = "contenteditable"
+
+ # Test html files.
+ _cursor_html = "layout/test_carets_cursor.html"
+
+ def setUp(self):
+ # Code to execute before every test is running.
+ super(AccessibleCaretCursorModeTestCase, self).setUp()
+ self.caret_tested_pref = "layout.accessiblecaret.enabled"
+ self.hide_carets_for_mouse = (
+ "layout.accessiblecaret.hide_carets_for_mouse_input"
+ )
+ self.prefs = {
+ self.caret_tested_pref: True,
+ self.hide_carets_for_mouse: False,
+ # To disable transition, or the caret may not be the desired
+ # location yet, we cannot press a caret successfully.
+ "layout.accessiblecaret.transition-duration": "0.0",
+ # Enabled hapticfeedback on all platforms. The tests shouldn't crash
+ # on platforms without hapticfeedback support.
+ "layout.accessiblecaret.hapticfeedback": True,
+ }
+ self.marionette.set_prefs(self.prefs)
+ self.actions = CaretActions(self.marionette)
+
+ def tearDown(self):
+ self.marionette.actions.release()
+ super(AccessibleCaretCursorModeTestCase, self).tearDown()
+
+ def open_test_html(self, test_html):
+ self.marionette.navigate(self.marionette.absolute_url(test_html))
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_move_cursor_to_the_right_by_one_character(self, el_id):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ target_content = sel.content
+ target_content = target_content[:1] + content_to_add + target_content[1:]
+
+ # Get first caret (x, y) at position 1 and 2.
+ el.tap()
+ sel.move_cursor_to_front()
+ cursor0_x, cursor0_y = sel.cursor_location()
+ first_caret0_x, first_caret0_y = sel.first_caret_location()
+ sel.move_cursor_by_offset(1)
+ first_caret1_x, first_caret1_y = sel.first_caret_location()
+
+ # Tap the front of the input to make first caret appear.
+ el.tap(cursor0_x, cursor0_y)
+
+ # Move first caret.
+ self.actions.flick(
+ el, first_caret0_x, first_caret0_y, first_caret1_x, first_caret1_y
+ ).perform()
+
+ self.actions.send_keys(content_to_add).perform()
+ self.assertEqual(target_content, sel.content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_move_cursor_to_end_by_dragging_caret_to_bottom_right_corner(self, el_id):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ target_content = sel.content + content_to_add
+
+ # Tap the front of the input to make first caret appear.
+ el.tap()
+ sel.move_cursor_to_front()
+ el.tap(*sel.cursor_location())
+
+ # Move first caret to the bottom-right corner of the element.
+ src_x, src_y = sel.first_caret_location()
+ dest_x, dest_y = el.rect["width"], el.rect["height"]
+ self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
+
+ self.actions.send_keys(content_to_add).perform()
+ self.assertEqual(target_content, sel.content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_move_cursor_to_front_by_dragging_caret_to_front(self, el_id):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ target_content = content_to_add + sel.content
+
+ # Get first caret location at the front.
+ el.tap()
+ sel.move_cursor_to_front()
+ dest_x, dest_y = sel.first_caret_location()
+
+ # Tap to make first caret appear. Note: it's strange that when the caret
+ # is at the end, the rect of the caret in <textarea> cannot be obtained.
+ # A bug perhaps.
+ el.tap()
+ sel.move_cursor_to_end()
+ sel.move_cursor_by_offset(1, backward=True)
+ el.tap(*sel.cursor_location())
+ src_x, src_y = sel.first_caret_location()
+
+ # Move first caret to the front of the input box.
+ self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
+
+ self.actions.send_keys(content_to_add).perform()
+ self.assertEqual(target_content, sel.content)
+
+ def test_caret_not_appear_when_typing_in_scrollable_content(self):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, self._input_id)
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ non_target_content = content_to_add + sel.content + string.ascii_letters
+
+ el.tap()
+ sel.move_cursor_to_end()
+
+ # Insert a long string to the end of the <input>, which triggers
+ # ScrollPositionChanged event.
+ el.send_keys(string.ascii_letters)
+
+ # The caret should not be visible. If it does appear wrongly due to the
+ # ScrollPositionChanged event, we can drag it to the front of the
+ # <input> to change the cursor position.
+ src_x, src_y = sel.first_caret_location()
+ dest_x, dest_y = 0, 0
+ self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
+
+ # The content should not be inserted at the front of the <input>.
+ el.send_keys(content_to_add)
+
+ self.assertNotEqual(non_target_content, sel.content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_input_padding_id, el_id=_input_padding_id)
+ @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_caret_not_jump_when_dragging_to_editable_content_boundary(self, el_id):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ non_target_content = sel.content + content_to_add
+
+ # Goal: the cursor position is not changed after dragging the caret down
+ # on the Y-axis.
+ el.tap()
+ sel.move_cursor_to_front()
+ el.tap(*sel.cursor_location())
+ x, y = sel.first_caret_location()
+
+ # Drag the caret down by 50px, and insert '!'.
+ self.actions.flick(el, x, y, x, y + 50).perform()
+ self.actions.send_keys(content_to_add).perform()
+ self.assertNotEqual(non_target_content, sel.content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_input_padding_id, el_id=_input_padding_id)
+ @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_caret_not_jump_to_front_when_dragging_up_to_editable_content_boundary(
+ self, el_id
+ ):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ non_target_content = content_to_add + sel.content
+
+ # Goal: the cursor position is not changed after dragging the caret down
+ # on the Y-axis.
+ el.tap()
+ sel.move_cursor_to_end()
+ sel.move_cursor_by_offset(1, backward=True)
+ el.tap(*sel.cursor_location())
+ x, y = sel.first_caret_location()
+
+ # Drag the caret up by 40px, and insert '!'.
+ self.actions.flick(el, x, y, x, y - 40).perform()
+ self.actions.send_keys(content_to_add).perform()
+ self.assertNotEqual(non_target_content, sel.content)
+
+ def test_drag_caret_from_front_to_end_across_columns(self):
+ self.open_test_html("layout/test_carets_columns.html")
+ el = self.marionette.find_element(By.ID, "columns-inner")
+ sel = SelectionManager(el)
+ content_to_add = "!"
+ target_content = sel.content + content_to_add
+
+ # Goal: the cursor position can be changed by dragging the caret from
+ # the front to the end of the content.
+
+ # Tap to make the cursor appear.
+ before_image_1 = self.marionette.find_element(By.ID, "before-image-1")
+ before_image_1.tap()
+
+ # Tap the front of the content to make first caret appear.
+ sel.move_cursor_to_front()
+ el.tap(*sel.cursor_location())
+ src_x, src_y = sel.first_caret_location()
+ dest_x, dest_y = el.rect["width"], el.rect["height"]
+
+ # Drag the first caret to the bottom-right corner of the element.
+ self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
+
+ self.actions.send_keys(content_to_add).perform()
+ self.assertEqual(target_content, sel.content)
+
+ def test_move_cursor_to_front_by_dragging_caret_to_front_br_element(self):
+ self.open_test_html(self._cursor_html)
+ el = self.marionette.find_element(By.ID, self._contenteditable_id)
+ sel = SelectionManager(el)
+ content_to_add_1 = "!"
+ content_to_add_2 = "\n\n"
+ target_content = content_to_add_1 + content_to_add_2 + sel.content
+
+ # Goal: the cursor position can be changed by dragging the caret from
+ # the end of the content to the front br element. Because we cannot get
+ # caret location if it's on a br element, we need to get the first caret
+ # location then adding the new lines.
+
+ # Get first caret location at the front.
+ el.tap()
+ sel.move_cursor_to_front()
+ dest_x, dest_y = sel.first_caret_location()
+
+ # Append new line to the front of the content.
+ el.send_keys(content_to_add_2)
+
+ # Tap to make first caret appear.
+ el.tap()
+ sel.move_cursor_to_end()
+ sel.move_cursor_by_offset(1, backward=True)
+ el.tap(*sel.cursor_location())
+ src_x, src_y = sel.first_caret_location()
+
+ # Move first caret to the front of the input box.
+ self.actions.flick(el, src_x, src_y, dest_x, dest_y).perform()
+
+ self.actions.send_keys(content_to_add_1).perform()
+ self.assertEqual(target_content, sel.content)
diff --git a/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py b/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py
new file mode 100644
index 0000000000..c9ac65e8ef
--- /dev/null
+++ b/layout/base/tests/marionette/test_accessiblecaret_selection_mode.py
@@ -0,0 +1,767 @@
+# -*- coding: utf-8 -*-
+# 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/.
+
+import re
+import sys
+import os
+
+# Add this directory to the import path.
+sys.path.append(os.path.dirname(__file__))
+
+from selection import (
+ CaretActions,
+ SelectionManager,
+)
+from marionette_driver.by import By
+from marionette_harness.marionette_test import (
+ MarionetteTestCase,
+ SkipTest,
+ parameterized,
+)
+
+
+class AccessibleCaretSelectionModeTestCase(MarionetteTestCase):
+ """Test cases for AccessibleCaret under selection mode."""
+
+ # Element IDs.
+ _input_id = "input"
+ _input_padding_id = "input-padding"
+ _input_size_id = "input-size"
+ _textarea_id = "textarea"
+ _textarea2_id = "textarea2"
+ _textarea_disabled_id = "textarea-disabled"
+ _textarea_one_line_id = "textarea-one-line"
+ _textarea_rtl_id = "textarea-rtl"
+ _contenteditable_id = "contenteditable"
+ _contenteditable2_id = "contenteditable2"
+ _content_id = "content"
+ _content2_id = "content2"
+ _non_selectable_id = "non-selectable"
+
+ # Test html files.
+ _selection_html = "layout/test_carets_selection.html"
+ _multipleline_html = "layout/test_carets_multipleline.html"
+ _multiplerange_html = "layout/test_carets_multiplerange.html"
+ _longtext_html = "layout/test_carets_longtext.html"
+ _iframe_html = "layout/test_carets_iframe.html"
+ _iframe_scroll_html = "layout/test_carets_iframe_scroll.html"
+ _display_none_html = "layout/test_carets_display_none.html"
+ _svg_shapes_html = "layout/test_carets_svg_shapes.html"
+
+ def setUp(self):
+ # Code to execute before every test is running.
+ super(AccessibleCaretSelectionModeTestCase, self).setUp()
+ self.carets_tested_pref = "layout.accessiblecaret.enabled"
+ self.prefs = {
+ "layout.word_select.eat_space_to_next_word": False,
+ self.carets_tested_pref: True,
+ # To disable transition, or the caret may not be the desired
+ # location yet, we cannot press a caret successfully.
+ "layout.accessiblecaret.transition-duration": "0.0",
+ # Enabled hapticfeedback on all platforms. The tests shouldn't crash
+ # on platforms without hapticfeedback support.
+ "layout.accessiblecaret.hapticfeedback": True,
+ }
+ self.marionette.set_prefs(self.prefs)
+ self.actions = CaretActions(self.marionette)
+
+ def tearDown(self):
+ self.marionette.actions.release()
+ super(AccessibleCaretSelectionModeTestCase, self).tearDown()
+
+ def open_test_html(self, test_html):
+ self.marionette.navigate(self.marionette.absolute_url(test_html))
+
+ def word_offset(self, text, ordinal):
+ "Get the character offset of the ordinal-th word in text."
+ tokens = re.split(r"(\S+)", text) # both words and spaces
+ spaces = tokens[0::2] # collect spaces at odd indices
+ words = tokens[1::2] # collect word at even indices
+
+ if ordinal >= len(words):
+ raise IndexError(
+ "Only %d words in text, but got ordinal %d" % (len(words), ordinal)
+ )
+
+ # Cursor position of the targeting word is behind the the first
+ # character in the word. For example, offset to 'def' in 'abc def' is
+ # between 'd' and 'e'.
+ offset = len(spaces[0]) + 1
+ offset += sum(len(words[i]) + len(spaces[i + 1]) for i in range(ordinal))
+ return offset
+
+ def test_word_offset(self):
+ text = " " * 3 + "abc" + " " * 3 + "def"
+
+ self.assertTrue(self.word_offset(text, 0), 4)
+ self.assertTrue(self.word_offset(text, 1), 10)
+ with self.assertRaises(IndexError):
+ self.word_offset(text, 2)
+
+ def word_location(self, el, ordinal):
+ """Get the location (x, y) of the ordinal-th word in el.
+
+ The ordinal starts from 0.
+
+ Note: this function has a side effect which changes focus to the
+ target element el.
+
+ """
+ sel = SelectionManager(el)
+ offset = self.word_offset(sel.content, ordinal)
+
+ # Move the blinking cursor to the word.
+ el.tap()
+ sel.move_cursor_to_front()
+ sel.move_cursor_by_offset(offset)
+ x, y = sel.cursor_location()
+
+ return x, y
+
+ def rect_relative_to_window(self, el):
+ """Get element's bounding rectangle.
+
+ This function is similar to el.rect, but the coordinate is relative to
+ the top left corner of the window instead of the document.
+
+ """
+ return self.marionette.execute_script(
+ """
+ let rect = arguments[0].getBoundingClientRect();
+ return {x: rect.x, y:rect.y, width: rect.width, height: rect.height};
+ """,
+ script_args=[el],
+ )
+
+ def long_press_on_location(self, el, x=None, y=None):
+ """Long press the location (x, y) to select a word.
+
+ If no (x, y) are given, it will be targeted at the center of the
+ element. On Windows, those spaces after the word will also be selected.
+ This function sends synthesized eMouseLongTap to gecko.
+
+ """
+ rect = self.rect_relative_to_window(el)
+ target_x = rect["x"] + (x if x is not None else rect["width"] // 2)
+ target_y = rect["y"] + (y if y is not None else rect["height"] // 2)
+
+ self.marionette.execute_script(
+ """
+ let utils = window.windowUtils;
+ utils.sendTouchEventToWindow('touchstart', [0],
+ [arguments[0]], [arguments[1]],
+ [1], [1], [0], [1], [0], [0], [0], 0);
+ utils.sendMouseEventToWindow('mouselongtap', arguments[0], arguments[1],
+ 0, 1, 0);
+ utils.sendTouchEventToWindow('touchend', [0],
+ [arguments[0]], [arguments[1]],
+ [1], [1], [0], [1], [0], [0], [0], 0);
+ """,
+ script_args=[target_x, target_y],
+ sandbox="system",
+ )
+
+ def long_press_on_word(self, el, wordOrdinal):
+ x, y = self.word_location(el, wordOrdinal)
+ self.long_press_on_location(el, x, y)
+
+ def to_unix_line_ending(self, s):
+ """Changes all Windows/Mac line endings in s to UNIX line endings."""
+
+ return s.replace("\r\n", "\n").replace("\r", "\n")
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_textarea_disabled_id, el_id=_textarea_disabled_id)
+ @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ @parameterized(_content_id, el_id=_content_id)
+ def test_long_press_to_select_a_word(self, el_id):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ self._test_long_press_to_select_a_word(el)
+
+ def _test_long_press_to_select_a_word(self, el):
+ sel = SelectionManager(el)
+ original_content = sel.content
+ words = original_content.split()
+ self.assertTrue(len(words) >= 2, "Expect at least two words in the content.")
+ target_content = words[0]
+
+ # Goal: Select the first word.
+ self.long_press_on_word(el, 0)
+
+ # Ignore extra spaces selected after the word.
+ self.assertEqual(target_content, sel.selected_content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_textarea_disabled_id, el_id=_textarea_disabled_id)
+ @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ @parameterized(_content_id, el_id=_content_id)
+ def test_drag_carets(self, el_id):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ original_content = sel.content
+ words = original_content.split()
+ self.assertTrue(len(words) >= 1, "Expect at least one word in the content.")
+
+ # Goal: Select all text after the first word.
+ target_content = original_content[len(words[0]) :]
+
+ # Get the location of the carets at the end of the content for later
+ # use.
+ el.tap()
+ sel.select_all()
+ end_caret_x, end_caret_y = sel.second_caret_location()
+
+ self.long_press_on_word(el, 0)
+
+ # Drag the second caret to the end of the content.
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
+
+ # Drag the first caret to the previous position of the second caret.
+ self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform()
+
+ self.assertEqual(target_content, sel.selected_content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_textarea_disabled_id, el_id=_textarea_disabled_id)
+ @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ @parameterized(_content_id, el_id=_content_id)
+ def test_drag_swappable_carets(self, el_id):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ original_content = sel.content
+ words = original_content.split()
+ self.assertTrue(len(words) >= 1, "Expect at least one word in the content.")
+
+ target_content1 = words[0]
+ target_content2 = original_content[len(words[0]) :]
+
+ # Get the location of the carets at the end of the content for later
+ # use.
+ el.tap()
+ sel.select_all()
+ end_caret_x, end_caret_y = sel.second_caret_location()
+
+ self.long_press_on_word(el, 0)
+
+ # Drag the first caret to the end and back to where it was
+ # immediately. The selection range should not be collapsed.
+ caret1_x, caret1_y = sel.first_caret_location()
+ self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y).flick(
+ el, end_caret_x, end_caret_y, caret1_x, caret1_y
+ ).perform()
+ self.assertEqual(target_content1, sel.selected_content)
+
+ # Drag the first caret to the end.
+ caret1_x, caret1_y = sel.first_caret_location()
+ self.actions.flick(el, caret1_x, caret1_y, end_caret_x, end_caret_y).perform()
+ self.assertEqual(target_content2, sel.selected_content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_textarea_disabled_id, el_id=_textarea_disabled_id)
+ @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ @parameterized(_content_id, el_id=_content_id)
+ def test_minimum_select_one_character(self, el_id):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ self._test_minimum_select_one_character(el)
+
+ @parameterized(_textarea2_id, el_id=_textarea2_id)
+ @parameterized(_contenteditable2_id, el_id=_contenteditable2_id)
+ @parameterized(_content2_id, el_id=_content2_id)
+ def test_minimum_select_one_character2(self, el_id):
+ self.open_test_html(self._multipleline_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ self._test_minimum_select_one_character(el)
+
+ def _test_minimum_select_one_character(self, el):
+ sel = SelectionManager(el)
+ original_content = sel.content
+ words = original_content.split()
+ self.assertTrue(len(words) >= 1, "Expect at least one word in the content.")
+
+ # Get the location of the carets at the end of the content for later
+ # use.
+ sel.select_all()
+ end_caret_x, end_caret_y = sel.second_caret_location()
+ el.tap()
+
+ # Goal: Select the first character.
+ target_content = original_content[0]
+
+ self.long_press_on_word(el, 0)
+
+ # Drag the second caret to the end of the content.
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(el, caret2_x, caret2_y, end_caret_x, end_caret_y).perform()
+
+ # Drag the second caret to the position of the first caret.
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform()
+
+ self.assertEqual(target_content, sel.selected_content)
+
+ @parameterized(
+ _input_id + "_to_" + _textarea_id, el1_id=_input_id, el2_id=_textarea_id
+ )
+ @parameterized(
+ _input_id + "_to_" + _contenteditable_id,
+ el1_id=_input_id,
+ el2_id=_contenteditable_id,
+ )
+ @parameterized(
+ _input_id + "_to_" + _content_id, el1_id=_input_id, el2_id=_content_id
+ )
+ @parameterized(
+ _textarea_id + "_to_" + _input_id, el1_id=_textarea_id, el2_id=_input_id
+ )
+ @parameterized(
+ _textarea_id + "_to_" + _contenteditable_id,
+ el1_id=_textarea_id,
+ el2_id=_contenteditable_id,
+ )
+ @parameterized(
+ _textarea_id + "_to_" + _content_id, el1_id=_textarea_id, el2_id=_content_id
+ )
+ @parameterized(
+ _contenteditable_id + "_to_" + _input_id,
+ el1_id=_contenteditable_id,
+ el2_id=_input_id,
+ )
+ @parameterized(
+ _contenteditable_id + "_to_" + _textarea_id,
+ el1_id=_contenteditable_id,
+ el2_id=_textarea_id,
+ )
+ @parameterized(
+ _contenteditable_id + "_to_" + _content_id,
+ el1_id=_contenteditable_id,
+ el2_id=_content_id,
+ )
+ @parameterized(
+ _content_id + "_to_" + _input_id, el1_id=_content_id, el2_id=_input_id
+ )
+ @parameterized(
+ _content_id + "_to_" + _textarea_id, el1_id=_content_id, el2_id=_textarea_id
+ )
+ @parameterized(
+ _content_id + "_to_" + _contenteditable_id,
+ el1_id=_content_id,
+ el2_id=_contenteditable_id,
+ )
+ def test_long_press_changes_focus_from(self, el1_id, el2_id):
+ self.open_test_html(self._selection_html)
+ el1 = self.marionette.find_element(By.ID, el1_id)
+ el2 = self.marionette.find_element(By.ID, el2_id)
+
+ # Compute the content of the first word in el2.
+ sel = SelectionManager(el2)
+ original_content = sel.content
+ words = original_content.split()
+ target_content = words[0]
+
+ # Goal: Tap to focus el1, and then select the first word on el2.
+
+ # We want to collect the location of the first word in el2 here
+ # since self.word_location() has the side effect which would
+ # change the focus.
+ x, y = self.word_location(el2, 0)
+
+ el1.tap()
+ self.long_press_on_location(el2, x, y)
+ self.assertEqual(target_content, sel.selected_content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_focus_not_changed_by_long_press_on_non_selectable(self, el_id):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ non_selectable = self.marionette.find_element(By.ID, self._non_selectable_id)
+
+ # Goal: Focus remains on the editable element el after long pressing on
+ # the non-selectable element.
+ sel = SelectionManager(el)
+ self.long_press_on_word(el, 0)
+ self.long_press_on_location(non_selectable)
+ active_sel = SelectionManager(self.marionette.get_active_element())
+ self.assertEqual(sel.content, active_sel.content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_textarea_id, el_id=_textarea_id)
+ @parameterized(_textarea_disabled_id, el_id=_textarea_disabled_id)
+ @parameterized(_textarea_rtl_id, el_id=_textarea_rtl_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ @parameterized(_content_id, el_id=_content_id)
+ def test_handle_tilt_when_carets_overlap_each_other(self, el_id):
+ """Test tilt handling when carets overlap to each other.
+
+ Let the two carets overlap each other. If they are set to tilted
+ successfully, tapping the tilted carets should not cause the selection
+ to be collapsed and the carets should be draggable.
+
+ """
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ original_content = sel.content
+ words = original_content.split()
+ self.assertTrue(len(words) >= 1, "Expect at least one word in the content.")
+
+ # Goal: Select the first word.
+ self.long_press_on_word(el, 0)
+ target_content = sel.selected_content
+
+ # Drag the first caret to the position of the second caret to trigger
+ # carets overlapping.
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(el, caret1_x, caret1_y, caret2_x, caret2_y).perform()
+
+ # We make two hit tests targeting the left edge of the left tilted caret
+ # and the right edge of the right tilted caret. If either of the hits is
+ # missed, selection would be collapsed and both carets should not be
+ # draggable.
+ (caret3_x, caret3_y), (caret4_x, caret4_y) = sel.carets_location()
+
+ # The following values are from ua.css and all.js
+ caret_width = float(self.marionette.get_pref("layout.accessiblecaret.width"))
+ caret_margin_left = float(
+ self.marionette.get_pref("layout.accessiblecaret.margin-left")
+ )
+ tilt_right_margin_left = 0.41 * caret_width
+ tilt_left_margin_left = -0.39 * caret_width
+
+ left_caret_left_edge_x = caret3_x + caret_margin_left + tilt_left_margin_left
+ el.tap(left_caret_left_edge_x + 2, caret3_y)
+
+ right_caret_right_edge_x = (
+ caret4_x + caret_margin_left + tilt_right_margin_left + caret_width
+ )
+ el.tap(right_caret_right_edge_x - 2, caret4_y)
+
+ # Drag the first caret back to the initial selection, the first word.
+ self.actions.flick(el, caret3_x, caret3_y, caret1_x, caret1_y).perform()
+
+ self.assertEqual(target_content, sel.selected_content)
+
+ def test_drag_caret_over_non_selectable_field(self):
+ """Test dragging the caret over a non-selectable field.
+
+ The selected content should exclude non-selectable elements and the
+ second caret should appear in last range's position.
+
+ """
+ self.open_test_html(self._multiplerange_html)
+ body = self.marionette.find_element(By.ID, "bd")
+ sel3 = self.marionette.find_element(By.ID, "sel3")
+ sel4 = self.marionette.find_element(By.ID, "sel4")
+ sel6 = self.marionette.find_element(By.ID, "sel6")
+
+ # Select target element and get target caret location
+ self.long_press_on_word(sel4, 3)
+ sel = SelectionManager(body)
+ end_caret_x, end_caret_y = sel.second_caret_location()
+
+ self.long_press_on_word(sel6, 0)
+ end_caret2_x, end_caret2_y = sel.second_caret_location()
+
+ # Select start element
+ self.long_press_on_word(sel3, 3)
+
+ # Drag end caret to target location
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(
+ body, caret2_x, caret2_y, end_caret_x, end_caret_y, 1
+ ).perform()
+ self.assertEqual(
+ self.to_unix_line_ending(sel.selected_content.strip()),
+ "this 3\nuser can select this",
+ )
+
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(
+ body, caret2_x, caret2_y, end_caret2_x, end_caret2_y, 1
+ ).perform()
+ self.assertEqual(
+ self.to_unix_line_ending(sel.selected_content.strip()),
+ "this 3\nuser can select this 4\nuser can select this 5\nuser",
+ )
+
+ # Drag first caret to target location
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(
+ body, caret1_x, caret1_y, end_caret_x, end_caret_y, 1
+ ).perform()
+ self.assertEqual(
+ self.to_unix_line_ending(sel.selected_content.strip()),
+ "4\nuser can select this 5\nuser",
+ )
+
+ def test_drag_swappable_caret_over_non_selectable_field(self):
+ self.open_test_html(self._multiplerange_html)
+ body = self.marionette.find_element(By.ID, "bd")
+ sel3 = self.marionette.find_element(By.ID, "sel3")
+ sel4 = self.marionette.find_element(By.ID, "sel4")
+ sel = SelectionManager(body)
+
+ self.long_press_on_word(sel4, 3)
+ (
+ (end_caret1_x, end_caret1_y),
+ (end_caret2_x, end_caret2_y),
+ ) = sel.carets_location()
+
+ self.long_press_on_word(sel3, 3)
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+
+ # Drag the first caret down, which will across the second caret.
+ self.actions.flick(
+ body, caret1_x, caret1_y, end_caret1_x, end_caret1_y
+ ).perform()
+ self.assertEqual(
+ self.to_unix_line_ending(sel.selected_content.strip()), "3\nuser can select"
+ )
+
+ # The old second caret becomes the first caret. Drag it down again.
+ self.actions.flick(
+ body, caret2_x, caret2_y, end_caret2_x, end_caret2_y
+ ).perform()
+ self.assertEqual(self.to_unix_line_ending(sel.selected_content.strip()), "this")
+
+ def test_drag_caret_to_beginning_of_a_line(self):
+ """Bug 1094056
+ Test caret visibility when caret is dragged to beginning of a line
+ """
+ self.open_test_html(self._multiplerange_html)
+ body = self.marionette.find_element(By.ID, "bd")
+ sel1 = self.marionette.find_element(By.ID, "sel1")
+ sel2 = self.marionette.find_element(By.ID, "sel2")
+
+ # Select the first word in the second line
+ self.long_press_on_word(sel2, 0)
+ sel = SelectionManager(body)
+ (
+ (start_caret_x, start_caret_y),
+ (end_caret_x, end_caret_y),
+ ) = sel.carets_location()
+
+ # Select target word in the first line
+ self.long_press_on_word(sel1, 2)
+
+ # Drag end caret to the beginning of the second line
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(
+ body, caret2_x, caret2_y, start_caret_x, start_caret_y
+ ).perform()
+
+ # Drag end caret back to the target word
+ self.actions.flick(
+ body, start_caret_x, start_caret_y, caret2_x, caret2_y
+ ).perform()
+
+ self.assertEqual(self.to_unix_line_ending(sel.selected_content), "select")
+
+ def test_select_word_inside_an_iframe(self):
+ """Bug 1088552
+ The scroll offset in iframe should be taken into consideration properly.
+ In this test, we scroll content in the iframe to the bottom to cause a
+ huge offset. If we use the right coordinate system, selection should
+ work. Otherwise, it would be hard to trigger select word.
+ """
+ self.open_test_html(self._iframe_html)
+ iframe = self.marionette.find_element(By.ID, "frame")
+
+ # switch to inner iframe and scroll to the bottom
+ self.marionette.switch_to_frame(iframe)
+ self.marionette.execute_script('document.getElementById("bd").scrollTop += 999')
+
+ # long press to select bottom text
+ body = self.marionette.find_element(By.ID, "bd")
+ sel = SelectionManager(body)
+ self._bottomtext = self.marionette.find_element(By.ID, "bottomtext")
+ self.long_press_on_location(self._bottomtext)
+
+ self.assertNotEqual(self.to_unix_line_ending(sel.selected_content), "")
+
+ def test_select_word_inside_an_unfocused_iframe(self):
+ """Bug 1306634: Test we can long press to select a word in an unfocused iframe."""
+ self.open_test_html(self._iframe_html)
+
+ el = self.marionette.find_element(By.ID, self._input_id)
+ sel = SelectionManager(el)
+
+ # First, we select the first word in the input of the parent document.
+ el_first_word = sel.content.split()[0] # first world is "ABC"
+ self.long_press_on_word(el, 0)
+ self.assertEqual(el_first_word, sel.selected_content)
+
+ # Then, we long press on the center of the iframe. It should select a
+ # word inside of the document, not the placehoder in the parent
+ # document.
+ iframe = self.marionette.find_element(By.ID, "frame")
+ self.long_press_on_location(iframe)
+ self.marionette.switch_to_frame(iframe)
+ body = self.marionette.find_element(By.ID, "bd")
+ sel = SelectionManager(body)
+ self.assertNotEqual("", sel.selected_content)
+
+ def test_carets_initialized_in_display_none(self):
+ """Test AccessibleCaretEventHub is properly initialized on a <html> with
+ display: none.
+
+ """
+ self.open_test_html(self._display_none_html)
+ html = self.marionette.find_element(By.ID, "html")
+ content = self.marionette.find_element(By.ID, "content")
+
+ # Remove 'display: none' from <html>
+ self.marionette.execute_script(
+ 'arguments[0].style.display = "unset";', script_args=[html]
+ )
+
+ # If AccessibleCaretEventHub is initialized successfully, select a word
+ # should work.
+ self._test_long_press_to_select_a_word(content)
+
+ def test_long_press_to_select_when_partial_visible_word_is_selected(self):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, self._input_size_id)
+ sel = SelectionManager(el)
+
+ original_content = sel.content
+ words = original_content.split()
+
+ # We cannot use self.long_press_on_word() for the second long press
+ # on the first word because it has side effect that changes the
+ # cursor position. We need to save the location of the first word to
+ # be used later.
+ word0_x, word0_y = self.word_location(el, 0)
+
+ # Long press on the second word.
+ self.long_press_on_word(el, 1)
+ self.assertEqual(words[1], sel.selected_content)
+
+ # Long press on the first word.
+ self.long_press_on_location(el, word0_x, word0_y)
+ self.assertEqual(words[0], sel.selected_content)
+
+ # If the second caret is visible, it can be dragged to the position
+ # of the first caret. After that, selection will contain only the
+ # first character.
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+ self.actions.flick(el, caret2_x, caret2_y, caret1_x, caret1_y).perform()
+ self.assertEqual(words[0][0], sel.selected_content)
+
+ @parameterized(_input_id, el_id=_input_id)
+ @parameterized(_input_padding_id, el_id=_input_padding_id)
+ @parameterized(_textarea_one_line_id, el_id=_textarea_one_line_id)
+ @parameterized(_contenteditable_id, el_id=_contenteditable_id)
+ def test_carets_not_jump_when_dragging_to_editable_content_boundary(self, el_id):
+ self.open_test_html(self._selection_html)
+ el = self.marionette.find_element(By.ID, el_id)
+ sel = SelectionManager(el)
+ original_content = sel.content
+ words = original_content.split()
+ self.assertTrue(len(words) >= 3, "Expect at least three words in the content.")
+
+ # Goal: the selection is not changed after dragging the caret on the
+ # Y-axis.
+ target_content = words[1]
+
+ self.long_press_on_word(el, 1)
+ (caret1_x, caret1_y), (caret2_x, caret2_y) = sel.carets_location()
+
+ # Drag the first caret up by 40px.
+ self.actions.flick(el, caret1_x, caret1_y, caret1_x, caret1_y - 40).perform()
+ self.assertEqual(target_content, sel.selected_content)
+
+ # Drag the second caret down by 50px.
+ self.actions.flick(el, caret2_x, caret2_y, caret2_x, caret2_y + 50).perform()
+ self.assertEqual(target_content, sel.selected_content)
+
+ def test_carets_should_not_appear_when_long_pressing_svg_shapes(self):
+ self.open_test_html(self._svg_shapes_html)
+
+ rect = self.marionette.find_element(By.ID, "rect")
+ text = self.marionette.find_element(By.ID, "text")
+
+ sel = SelectionManager(text)
+ num_words_in_text = len(sel.content.split())
+
+ # Goal: the carets should not appear when long-pressing on the
+ # unselectable SVG rect.
+
+ # Get the position of the end of last word in text, i.e. the
+ # position of the second caret when selecting the last word.
+ self.long_press_on_word(text, num_words_in_text - 1)
+ (_, _), (x2, y2) = sel.carets_location()
+
+ # Long press to select the unselectable SVG rect.
+ self.long_press_on_location(rect)
+ (_, _), (x, y) = sel.carets_location()
+
+ # Drag the second caret to (x2, y2).
+ self.actions.flick(text, x, y, x2, y2).perform()
+
+ # If the carets should appear on the rect, the selection will be
+ # extended to cover all the words in text. Assert this should not
+ # happen.
+ self.assertNotEqual(sel.content, sel.selected_content.strip())
+
+ def test_select_word_scroll_then_drag_caret(self):
+ """Bug 1657256: Test select word, scroll page up , and then drag the second
+ caret down to cover "EEEEEE".
+
+ Note the selection should be extended to just cover "EEEEEE", not extend
+ to other lines below "EEEEEE".
+
+ """
+
+ self.open_test_html(self._iframe_scroll_html)
+ iframe = self.marionette.find_element(By.ID, "iframe")
+
+ # Switch to the inner iframe.
+ self.marionette.switch_to_frame(iframe)
+ body = self.marionette.find_element(By.ID, "bd")
+ sel = SelectionManager(body)
+
+ # Select "EEEEEE" to get the y position of the second caret. This is the
+ # y position we are going to drag the caret to.
+ content2 = self.marionette.find_element(By.ID, self._content2_id)
+ self.long_press_on_word(content2, 0)
+ (_, _), (x, y2) = sel.carets_location()
+
+ # Step 1: Select "DDDDDD".
+ content = self.marionette.find_element(By.ID, self._content_id)
+ self.long_press_on_word(content, 0)
+ (_, _), (_, y1) = sel.carets_location()
+
+ # The y-axis offset of the second caret needed to extend the selection.
+ y_offset = y2 - y1
+
+ # Step 2: Scroll the page upwards by 40px.
+ scroll_offset = 40
+ self.marionette.execute_script(
+ "document.documentElement.scrollTop += arguments[0]",
+ script_args=[scroll_offset],
+ )
+
+ # Step 3: Drag the second caret down.
+ self.actions.flick(
+ body, x, y1 - scroll_offset, x, y1 - scroll_offset + y_offset
+ ).perform()
+
+ self.assertEqual("DDDDDD EEEEEE", sel.selected_content)
diff --git a/layout/base/tests/mochitest.ini b/layout/base/tests/mochitest.ini
new file mode 100644
index 0000000000..2c7ce049df
--- /dev/null
+++ b/layout/base/tests/mochitest.ini
@@ -0,0 +1,470 @@
+[DEFAULT]
+prefs =
+ formhelper.autozoom.force-disable.test-only=true
+support-files =
+ Ahem.ttf
+ file_bug842853.html
+ file_bug842853.sjs
+ selection-utils.js
+ !/gfx/layers/apz/test/mochitest/apz_test_utils.js
+
+[test_accessiblecaret_magnifier.html]
+support-files = accessiblecaret_magnifier.html
+[test_after_paint_pref.html]
+[test_border_radius_hit_testing.html]
+support-files = border_radius_hit_testing_iframe.html
+[test_bug66619.html]
+[test_bug93077-1.html]
+[test_bug93077-2.html]
+[test_bug93077-3.html]
+[test_bug93077-4.html]
+[test_bug93077-5.html]
+[test_bug93077-6.html]
+[test_bug114649.html]
+[test_bug332655-1.html]
+[test_bug332655-2.html]
+skip-if = win10_2004 && bits == 32 # Bug 1727925
+[test_bug369950.html]
+skip-if = true # Bug 492575
+support-files = bug369950-subframe.xml
+[test_bug370436.html]
+[test_bug386575.xhtml]
+[test_bug388019.html]
+[test_bug394057.html]
+skip-if = toolkit == 'android' # Bug 1355817
+[test_bug399284.html]
+[test_bug399951.html]
+[test_bug404209.xhtml]
+[test_bug416896.html]
+[test_bug423523.html]
+[test_bug435293-interaction.html]
+[test_bug435293-scale.html]
+[test_bug435293-skew.html]
+[test_bug449781.html]
+[test_bug450930.xhtml]
+skip-if = true # bug 934301
+support-files = bug450930.xhtml
+[test_bug469170.html]
+[test_bug471126.html]
+[test_bug499538-1.html]
+[test_bug514127.html]
+[test_bug518777.html]
+[test_bug548545.xhtml]
+[test_bug558663.html]
+support-files = bug558663.html
+[test_bug559499.html]
+[test_bug569520.html]
+[test_bug582181-1.html]
+[test_bug582181-2.html]
+[test_bug582771.html]
+[test_bug583889.html]
+support-files =
+ bug583889_inner1.html
+ bug583889_inner2.html
+[test_bug588174.html]
+[test_bug603550.html]
+fail-if = (xorigin && fission)
+[test_bug607529.html]
+support-files =
+ file_bug607529.html
+ file_bug607529-1.html
+[test_bug629838.html]
+skip-if = toolkit == 'android' # android: Requires plugin support
+[test_bug644768.html]
+[test_bug646757.html]
+[test_bug667512.html]
+[test_bug677878.html]
+[test_bug687297.html]
+support-files =
+ bug687297_a.html
+ bug687297_b.html
+ bug687297_c.html
+[test_bug696020.html]
+[test_bug718809.html]
+[test_bug725426.html]
+[test_bug731777.html]
+[test_bug749186.html]
+[test_bug761572.html]
+[test_bug770106.html]
+[test_bug842853.html]
+support-files =
+ file_bug842853-frame.html
+[test_bug842853-2.html]
+[test_bug849219.html]
+[test_bug851445.html]
+skip-if = toolkit == 'android' # Bug 1355821
+support-files = bug851445_helper.html
+[test_bug851485.html]
+skip-if =
+ toolkit == 'android' # Bug 1355821
+[test_bug858459.html]
+skip-if = toolkit == 'android' # Bug 1355822
+[test_bug970964.html]
+support-files =
+ bug970964_inner.html
+ bug970964_inner2.html
+[test_bug977003.html]
+support-files =
+ bug977003_inner_1.html
+ bug977003_inner_2.html
+ bug977003_inner_3.html
+ bug977003_inner_4.html
+ bug977003_inner_5.html
+ bug977003_inner_6.html
+[test_bug990340.html]
+[test_bug993936.html]
+[test_bug1078327.html]
+support-files = bug1078327_inner.html
+[test_bug1080360.html]
+support-files = bug1080360_inner.html
+[test_bug1080361.html]
+support-files = bug1080361_inner.html
+[test_bug1093686.html]
+support-files = bug1093686_inner.html
+[test_bug1120705.html]
+skip-if = toolkit == 'android' || os == 'mac' || toolkit == 'gtk' # android does not have clickable scrollbars, mac does not have scrollbar down and up buttons, gtk may or may not have scrollbar buttons depending on theme
+[test_bug1153130.html]
+support-files = bug1153130_inner.html
+[test_bug1162990.html]
+support-files =
+ bug1162990_inner_1.html
+ bug1162990_inner_2.html
+[test_bug1216483.html]
+[test_bug1226904.html]
+support-files = bug1226904.html
+[test_bug1246622.html]
+[test_bug1278021.html]
+[test_bug1448730.html]
+support-files = bug1448730.html
+[test_bug1515822.html]
+[test_bug1550869_video.html]
+[test_bug1714640.html]
+[test_bug1756118.html]
+[test_caret_browsing_around_form_controls.html]
+skip-if = toolkit == 'android'
+[test_dynamic_toolbar_max_height.html]
+support-files = file_dynamic_toolbar_max_height.html
+[test_emulateMedium.html]
+[test_emulate_color_scheme.html]
+[test_event_target_radius.html]
+skip-if = xorigin # JavaScript error: resource://specialpowers/SpecialPowersChild.sys.mjs, line 73: SecurityError: Permission denied to access property "windowUtils" on cross-origin object
+[test_frame_reconstruction_for_column_span.html]
+[test_frame_reconstruction_for_pseudo_elements.html]
+[test_frame_reconstruction_for_svg_transforms.html]
+[test_frame_reconstruction_scroll_restore.html]
+[test_frame_reconstruction_body_writing_mode.html]
+[test_frame_reconstruction_body_table.html]
+[test_getBoxQuads_convertPointRectQuad.html]
+support-files =
+ file_getBoxQuads_convertPointRectQuad_frame1.html
+ file_getBoxQuads_convertPointRectQuad_frame2.html
+[test_getClientRects_emptytext.html]
+[test_mozPaintCount.html]
+skip-if = toolkit == 'android' # android: Requires plugin support
+[test_partialbg.html]
+support-files =
+ sendimagenevercomplete.sjs
+ partial.png
+skip-if =
+ http3
+[test_preserve3d_sorting_hit_testing.html]
+support-files = preserve3d_sorting_hit_testing_iframe.html
+[test_preserve3d_sorting_hit_testing2.html]
+support-files = preserve3d_sorting_hit_testing2_iframe.html
+[test_refreshDriver_hasPendingTick.html]
+[test_reftests_with_caret.html]
+skip-if =
+ tsan # Bug 1612707
+support-files =
+ bug106855-1.html
+ bug106855-2.html
+ bug106855-1-ref.html
+ bug240933-1.html
+ bug240933-2.html
+ bug240933-1-ref.html
+ bug389321-1.html
+ bug389321-1-ref.html
+ bug389321-2.html
+ bug389321-2-ref.html
+ bug389321-3.html
+ bug389321-3-ref.html
+ bug482484.html
+ bug482484-ref.html
+ bug503399.html
+ bug503399-ref.html
+ bug512295-1.html
+ bug512295-1-ref.html
+ bug512295-2.html
+ bug512295-2-ref.html
+ bug585922.html
+ bug585922-ref.html
+ bug597519-1.html
+ bug597519-1-ref.html
+ bug602141-1.html
+ bug602141-1-ref.html
+ bug602141-2.html
+ bug602141-2-ref.html
+ bug602141-3.html
+ bug602141-3-ref.html
+ bug602141-4.html
+ bug602141-4-ref.html
+ bug612271-1.html
+ bug612271-2.html
+ bug612271-3.html
+ bug612271-ref.html
+ bug613433-1.html
+ bug613433-2.html
+ bug613433-3.html
+ bug613433-ref.html
+ bug613807-1.html
+ bug613807-1-ref.html
+ bug632215-1.html
+ bug632215-2.html
+ bug632215-ref.html
+ bug633044-1.html
+ bug633044-1-ref.html
+ bug634406-1.html
+ bug634406-1-ref.html
+ bug644428-1.html
+ bug644428-1-ref.html
+ bug646382-1.html
+ bug646382-1-ref.html
+ bug646382-2.html
+ bug646382-2-ref.html
+ bug664087-1.html
+ bug664087-1-ref.html
+ bug664087-2.html
+ bug664087-2-ref.html
+ bug682712-1.html
+ bug682712-1-ref.html
+ bug746993-1.html
+ bug746993-1-ref.html
+ bug923376.html
+ bug923376-ref.html
+ bug956530-1.html
+ bug956530-1-ref.html
+ bug966992-1.html
+ bug966992-1-ref.html
+ bug966992-2.html
+ bug966992-2-ref.html
+ bug966992-3.html
+ bug966992-3-ref.html
+ bug989012-1.html
+ bug989012-1-ref.html
+ bug989012-2.html
+ bug989012-2-ref.html
+ bug989012-3.html
+ bug989012-3-ref.html
+ bug1007065-1.html
+ bug1007065-1-ref.html
+ bug1007067-1.html
+ bug1007067-1-ref.html
+ bug1061468.html
+ bug1061468-ref.html
+ bug1082486-1.html
+ bug1082486-1-ref.html
+ bug1082486-2.html
+ bug1082486-2-ref.html
+ bug1097242-1.html
+ bug1097242-1-ref.html
+ bug1109968-1-ref.html
+ bug1109968-1.html
+ bug1109968-2-ref.html
+ bug1109968-2.html
+ bug1123067-1.html
+ bug1123067-2.html
+ bug1123067-3.html
+ bug1123067-ref.html
+ bug1132768-1.html
+ bug1132768-1-ref.html
+ bug1237236-1.html
+ bug1237236-1-ref.html
+ bug1237236-2.html
+ bug1237236-2-ref.html
+ bug1258308-1.html
+ bug1258308-1-ref.html
+ bug1258308-2.html
+ bug1258308-2-ref.html
+ bug1259949-1.html
+ bug1259949-1-ref.html
+ bug1259949-2.html
+ bug1259949-2-ref.html
+ bug1263288.html
+ bug1263288-ref.html
+ bug1263357-1.html
+ bug1263357-1-ref.html
+ bug1263357-2.html
+ bug1263357-2-ref.html
+ bug1263357-3.html
+ bug1263357-3-ref.html
+ bug1263357-4.html
+ bug1263357-4-ref.html
+ bug1263357-5.html
+ bug1263357-5-ref.html
+ bug1354478-1.html
+ bug1354478-1-ref.html
+ bug1354478-2.html
+ bug1354478-2-ref.html
+ bug1354478-3.html
+ bug1354478-3-ref.html
+ bug1354478-4.html
+ bug1354478-4-ref.html
+ bug1354478-5.html
+ bug1354478-5-ref.html
+ bug1354478-6.html
+ bug1354478-6-ref.html
+ bug1359411.html
+ bug1359411-ref.html
+ bug1415416.html
+ bug1415416-ref.html
+ bug1423331-1.html
+ bug1423331-1-ref.html
+ bug1423331-2.html
+ bug1423331-2-ref.html
+ bug1423331-3.html
+ bug1423331-4.html
+ bug1484094-1.html
+ bug1484094-1-ref.html
+ bug1484094-2.html
+ bug1484094-2-ref.html
+ bug1496118.html
+ bug1496118-ref.html
+ bug1510942-1.html
+ bug1510942-1-ref.html
+ bug1510942-2.html
+ bug1510942-2-ref.html
+ bug1516963-1.html
+ bug1516963-1-ref.html
+ bug1516963-2.html
+ bug1516963-2-ref.html
+ bug1516963-3.html
+ bug1516963-3-ref.html
+ bug1516963-4.html
+ bug1516963-4-ref.html
+ bug1516963-5.html
+ bug1516963-5-ref.html
+ bug1516963-6.html
+ bug1516963-6-ref.html
+ bug1524266-1.html
+ bug1524266-1-ref.html
+ bug1524266-2.html
+ bug1524266-2-ref.html
+ bug1524266-3.html
+ bug1524266-4.html
+ bug1550869-1a.html
+ bug1550869-1b.html
+ bug1550869-1c.html
+ bug1550869-1-ref.html
+ bug1550869-2a.html
+ bug1550869-2b.html
+ bug1550869-2c.html
+ bug1550869-2d.html
+ bug1550869-2-ref.html
+ bug1591282-1.html
+ bug1591282-1-ref.html
+ bug1611661.html
+ bug1611661-ref.html
+ bug1634543-1.html
+ bug1634543-1-ref.html
+ bug1634543-2.html
+ bug1634543-3.html
+ bug1634543-4.html
+ bug1634743-1.html
+ bug1634743-1-ref.html
+ bug1637476-1.html
+ bug1637476-1-ref.html
+ bug1637476-2.html
+ bug1637476-2-ref.html
+ bug1637476-3.html
+ bug1637476-3-ref.html
+ bug1663475-1.html
+ bug1663475-1-ref.html
+ bug1663475-2.html
+ bug1663475-2-ref.html
+ bug1670531-1.html
+ bug1670531-2.html
+ bug1670531-3.html
+ bug1670531-3-ref.html
+ bug1670531-4.html
+ collapse-selection-into-editing-host-during-blur-of-input.html
+ collapse-selection-into-editing-host-during-blur-of-input-ref.html
+ image_rgrg-256x256.png
+ input-invalid-ref.html
+ input-maxlength-invalid-change.html
+ input-maxlength-ui-invalid-change.html
+ input-maxlength-ui-valid-change.html
+ input-maxlength-valid-before-change.html
+ input-maxlength-valid-change.html
+ input-minlength-invalid-change.html
+ input-minlength-ui-invalid-change.html
+ input-minlength-ui-valid-change.html
+ input-minlength-valid-before-change.html
+ input-minlength-valid-change.html
+ input-password-remask.html
+ input-password-remask-ref.html
+ input-password-RTL-input.html
+ input-password-RTL-input-ref.html
+ input-password-unmask.html
+ input-password-unmask-around-emoji.html
+ input-password-unmask-around-emoji-ref.html
+ input-password-unmask-ref.html
+ input-stoppropagation.html
+ input-stoppropagation-ref.html
+ input-valid-ref.html
+ interlinePosition-after-Selection-addRange.html
+ interlinePosition-after-Selection-addRange-ref.html
+ multi-range-script-select.html
+ multi-range-script-select-ref.html
+ multi-range-user-select.html
+ multi-range-user-select-ref.html
+ textarea-invalid-ref.html
+ textarea-maxlength-invalid-change.html
+ textarea-maxlength-ui-invalid-change.html
+ textarea-maxlength-ui-valid-change.html
+ textarea-maxlength-valid-before-change.html
+ textarea-maxlength-valid-change.html
+ textarea-minlength-invalid-change.html
+ textarea-minlength-ui-invalid-change.html
+ textarea-minlength-ui-valid-change.html
+ textarea-minlength-valid-before-change.html
+ textarea-minlength-valid-change.html
+ textarea-valid-ref.html
+ bug1506547-1.html
+ bug1506547-2.html
+ bug1506547-3.html
+ bug1506547-4.html
+ bug1506547-5.html
+ bug1506547-6.html
+ bug1506547-4-ref.html
+ bug1506547-5-ref.html
+ bug1518339-1.html
+ bug1518339-1-ref.html
+ bug1518339-2.html
+ bug1518339-2-ref.html
+ bug1529492-1.html
+ bug1529492-1-ref.html
+ chrome/blue-32x32.png
+[test_resize_flush.html]
+support-files = resize_flush_iframe.html
+[test_scroll_event_ordering.html]
+[test_scroll_space_no_range_overflow_scroll.html]
+[test_scroll_selection_into_view.html]
+skip-if = toolkit == 'android' # Bug 1355844
+support-files =
+ scroll_selection_into_view_window.html
+ scroll_selection_into_view_window_frame.html
+[test_synthmousemove.html]
+support-files =
+ helper_synthmousemove.html
+ file_synthmousemove.html
+[test_transformed_scrolling_repaints.html]
+[test_transformed_scrolling_repaints_2.html]
+skip-if = (headless && os == 'mac') # Headless Bug 1414103
+[test_transformed_scrolling_repaints_3.html]
+support-files =
+ transformed_scrolling_repaints_3_window.html
+ transformed_scrolling_repaints_3_window_frame.html
+[test_zoom_restore_bfcache.html]
+skip-if =
+ fission && os == "android" # Bug 1827329
+support-files = file_zoom_restore_bfcache.html
diff --git a/layout/base/tests/multi-range-script-select-ref.html b/layout/base/tests/multi-range-script-select-ref.html
new file mode 100644
index 0000000000..509f5e4ae3
--- /dev/null
+++ b/layout/base/tests/multi-range-script-select-ref.html
@@ -0,0 +1,173 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1129078</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="selection-utils.js"></script>
+
+<style type="text/css">
+@font-face {
+ font-family: Ahem;
+ src: url("Ahem.ttf");
+}
+html,body { margin:0; padding: 0; }
+body,pre { font-family: Ahem; font-size: 20px; }
+</style>
+</head>
+<body>
+
+<pre id="select">
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+</pre>
+
+<pre id="log" style="border:1px solid green"></pre>
+
+<script>
+
+var sel = window.getSelection();
+var e = document.querySelector('#select');
+function setupSelectionPrev3() {
+ addChildRanges([[0,150,0,160], [0,65,0,70], [0,15,0,15]], e);
+ sel.extend(e.firstChild, 10); // to get eDirPrevious direction
+}
+function setupSelectionPrev2() {
+ addChildRanges([[0,150,0,160], [0,70,0,70]], e);
+ sel.extend(e.firstChild, 65); // to get eDirPrevious direction
+}
+function setupSelectionPrev1() {
+ addChildRanges([[0,160,0,160]], e);
+ sel.extend(e.firstChild, 150); // to get eDirPrevious direction
+}
+
+function setupSelectionNext3() {
+ addChildRanges([[0,10,0,15], [0,65,0,70], [0,150,0,160]], e);
+}
+function setupSelectionNext2() {
+ addChildRanges([[0,10,0,15], [0,65,0,70]], e);
+}
+function setupSelectionNext2b() {
+ addChildRanges([[0,15,0,80], [0,150,0,160]], e);
+}
+function setupSelectionNext1() {
+ addChildRanges([[0,10,0,15]], e);
+}
+function setupSelectionNext1b() {
+ addChildRanges([[0,15,0,170]], e);
+}
+function setupSelectionNext1c() {
+ addChildRanges([[0,150,0,160]], e);
+}
+
+function runTest() {
+ var hash = window.location.hash
+ var op = hash.substring(6,8);
+ var test = hash.substring(0,6);
+ if (hash.substring(0,5) == "#prev") {
+ if (test == "#prev1") {
+ setupSelectionPrev3();
+ if (op == "SL") {
+ sel.extend(e.firstChild, 8);
+ } else if (op == "SR") {
+ sel.extend(e.firstChild, 12);
+ } else if (op == "AD") {
+ addChildRanges([[0,1,0,2]], e);
+ } else {
+ sel.extend(e.firstChild, 1);
+ }
+ } else if (test == "#prev2") {
+ setupSelectionPrev3();
+ sel.extend(e.firstChild, 13);
+ } else if (test == "#prev3") {
+ if (op == "S_") {
+ setupSelectionPrev3();
+ sel.extend(e.firstChild, 20);
+ } else if (op == "SA") {
+ setupSelectionPrev3();
+ sel.extend(e.firstChild, 20);
+ }
+ } else if (test == "#prev4") {
+ addChildRanges([[0,67,0,70], [0,150,0,160], [0,15,0,67]], e);
+ } else if (test == "#prev5") {
+ if (op == "S_") {
+ setupSelectionNext2b();
+ } else if (op == "SA") {
+ setupSelectionNext2b();
+ }
+ } else if (test == "#prev6") {
+ addChildRanges([[0,152,0,160], [0,15,0,152]], e);
+ } else if (test == "#prev7") {
+ if (op == "AD") {
+ setupSelectionPrev3();
+ addChildRanges([[0,168,0,170]], e);
+ } else if (op == "S_") {
+ setupSelectionNext1b();
+ } else if (op == "SA") {
+ setupSelectionNext1b();
+ }
+ }
+ } else {
+ if (test == "#next1") {
+ if (op == "SL") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 158);
+ } else if (op == "SR") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 162);
+ } else if (op == "AD") {
+ setupSelectionNext3();
+ addChildRanges([[0,1,0,2]], e);
+ } else if (op == "S_") {
+ setupSelectionNext1c();
+ sel.extend(e.firstChild, 1);
+ } else if (op == "SA") {
+ setupSelectionNext1c();
+ sel.extend(e.firstChild, 1);
+ }
+ } else if (test == "#next2") {
+ addChildRanges([[0,10,0,13], [0,150,0,151]], e);
+ sel.extend(e.firstChild, 13);
+ } else if (test == "#next3") {
+ if (op == "S_") {
+ addChildRanges([[0,10,0,15], [0,150,0,151]], e);
+ sel.extend(e.firstChild, 20);
+ } else if (op == "SA") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 20);
+ }
+ } else if (test == "#next4") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 67);
+ } else if (test == "#next5") {
+ if (op == "S_") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 80);
+ } else if (op == "SA") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 80);
+ }
+ } else if (test == "#next6") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 152);
+ } else if (test == "#next7") {
+ setupSelectionNext3();
+ if (op == "AD") {
+ addChildRanges([[0,168,0,170]], e);
+ } else {
+ sel.extend(e.firstChild, 170);
+ }
+ }
+ }
+ document.documentElement.removeAttribute("class");
+}
+
+SimpleTest.waitForFocus(runTest);
+
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/multi-range-script-select.html b/layout/base/tests/multi-range-script-select.html
new file mode 100644
index 0000000000..e5ad7d2e77
--- /dev/null
+++ b/layout/base/tests/multi-range-script-select.html
@@ -0,0 +1,185 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1129078</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="selection-utils.js"></script>
+
+<style type="text/css">
+@font-face {
+ font-family: Ahem;
+ src: url("Ahem.ttf");
+}
+html,body { margin:0; padding: 0; }
+body,pre { font-family: Ahem; font-size: 20px; }
+</style>
+</head>
+<body>
+
+<pre id="select">
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+</pre>
+
+<pre id="log" style="border:1px solid green"></pre>
+
+<script>
+window.info = parent.info;
+window.is = parent.is;
+window.isnot = parent.isnot;
+window.ok = parent.ok;
+
+function setupPrevSelection() {
+ var sel = window.getSelection();
+ var e = document.querySelector('#select');
+ addChildRanges([[0,150,0,160], [0,65,0,70], [0,15,0,15]], e);
+ sel.extend(e.firstChild, 10); // to get eDirPrevious direction
+}
+
+function setupNextSelection() {
+ var sel = window.getSelection();
+ var e = document.querySelector('#select');
+ addChildRanges([[0,10,0,15], [0,65,0,70], [0,150,0,160]], e);
+}
+
+var ops = {
+ S_ : shiftClick,
+ SA : shiftAccelClick,
+ AD : accelDragSelect,
+ SL : keyLeft,
+ SR : keyRight
+}
+
+function runTest() {
+ var e = document.querySelector('#select');
+ var hash = window.location.hash
+ if (hash.substring(0,5)=="#prev")
+ setupPrevSelection();
+ else
+ setupNextSelection();
+ var op = hash.substring(6,8);
+ var action = ops[op];
+ var test = hash.substring(0,6);
+ if (hash.substring(0,5) == "#prev") {
+ if (test == "#prev1") {
+ if (action == keyLeft) {
+ keyLeft({shiftKey:true}, 2)
+ checkRanges([[0,8,0,15], [0,65,0,70], [0,150,0,160]], e);
+ } else if (action == keyRight) {
+ keyRight({shiftKey:true}, 2)
+ checkRanges([[0,12,0,15], [0,65,0,70], [0,150,0,160]], e);
+ } else if (action == accelDragSelect) {
+ accelDragSelect(e, 30, 50);
+ checkRanges([[0,1,0,2], [0,10,0,15], [0,65,0,70], [0,150,0,160]], e);
+ } else {
+ action(e, 30);
+ checkRanges([[0,1,0,15], [0,65,0,70], [0,150,0,160]], e);
+ }
+ } else if (test == "#prev2") {
+ action(e, 260);
+ checkRanges([[0,13,0,15], [0,65,0,70], [0,150,0,160]], e);
+ } else if (test == "#prev3") {
+ action(e, 400);
+ if (action == shiftClick)
+ checkRanges([[0,15,0,20], [0,65,0,70], [0,150,0,160]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,15,0,20], [0,65,0,70], [0,150,0,160]], e);
+ } else if (test == "#prev4") {
+ action(e, 180, 65);
+ if (action == shiftClick)
+ checkRanges([[0,15,0,67], [0,67,0,70], [0,150,0,160]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,15,0,67], [0,67,0,70], [0,150,0,160]], e);
+ } else if (test == "#prev5") {
+ action(e, 440, 65);
+ if (action == shiftClick)
+ checkRanges([[0,15,0,80], [0,150,0,160]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,15,0,80], [0,150,0,160]], e);
+ } else if (test == "#prev6") {
+ action(e, 140, 125);
+ if (action == shiftClick)
+ checkRanges([[0,15,0,152], [0,152,0,160]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,15,0,152], [0,152,0,160]], e);
+ } else if (test == "#prev7") {
+ if (action == accelDragSelect) {
+ accelDragSelect(e, 460, 500, 125);
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,160], [0,168,0,170]], e);
+ } else if (action == shiftClick) {
+ action(e, 500, 125);
+ checkRanges([[0,15,0,170]], e);
+ } else if (action == shiftAccelClick) {
+ action(e, 500, 125);
+ checkRanges([[0,15,0,170]], e);
+ }
+ }
+ } else {
+ if (test == "#next1") {
+ if (action == keyLeft) {
+ keyLeft({shiftKey:true}, 2)
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,158]], e);
+ } else if (action == keyRight) {
+ keyRight({shiftKey:true}, 2)
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,162]], e);
+ } else if (action == accelDragSelect) {
+ accelDragSelect(e, 30, 50);
+ checkRanges([[0,1,0,2], [0,10,0,15], [0,65,0,70], [0,150,0,160]], e);
+ } else {
+ action(e, 30);
+ checkRanges([[0,1,0,150]], e);
+ }
+ } else if (test == "#next2") {
+ action(e, 260);
+ checkRanges([[0,10,0,13], [0,13,0,150]], e);
+ } else if (test == "#next3") {
+ action(e, 400);
+ if (action == shiftClick)
+ checkRanges([[0,10,0,15], [0,20,0,150]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,10,0,15], [0,20,0,150]], e);
+ } else if (test == "#next4") {
+ action(e, 180, 65);
+ if (action == shiftClick)
+ checkRanges([[0,10,0,15], [0,65,0,67], [0,67,0,150]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,10,0,15], [0,65,0,67], [0,67,0,150]], e);
+ } else if (test == "#next5") {
+ action(e, 440, 65);
+ if (action == shiftClick)
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,80,0,150]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,80,0,150]], e);
+ } else if (test == "#next6") {
+ action(e, 140, 125);
+ if (action == shiftClick)
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,152]], e);
+ else if (action == shiftAccelClick)
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,152]], e);
+ } else if (test == "#next7") {
+ if (action == accelDragSelect) {
+ accelDragSelect(e, 460, 500, 125);
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,160], [0,168,0,170]], e);
+ } else if (action == shiftClick) {
+ action(e, 500, 125);
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,170]], e);
+ } else if (action == shiftAccelClick) {
+ action(e, 500, 125);
+ checkRanges([[0,10,0,15], [0,65,0,70], [0,150,0,170]], e);
+ }
+ }
+ }
+ document.documentElement.removeAttribute("class");
+}
+
+SimpleTest.waitForFocus(runTest);
+
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/multi-range-user-select-ref.html b/layout/base/tests/multi-range-user-select-ref.html
new file mode 100644
index 0000000000..00d7d41269
--- /dev/null
+++ b/layout/base/tests/multi-range-user-select-ref.html
@@ -0,0 +1,166 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1129078</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="selection-utils.js"></script>
+
+<style type="text/css">
+@font-face {
+ font-family: Ahem;
+ src: url("Ahem.ttf");
+}
+html,body { margin:0; padding: 0; }
+body,pre { font-family: Ahem; font-size: 20px; }
+</style>
+</head>
+<body>
+
+<pre id="select">
+2af45494-ak7e-11e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae4-ci5c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a96319c8-ad7d-11e4-b312-039c
+</pre>
+
+<pre id="log" style="border:1px solid green"></pre>
+
+<script>
+
+var sel = window.getSelection();
+var e = document.querySelector('#select');
+function setupSelectionPrev3() {
+ addChildRanges([[0,150,0,160], [0,65,0,70], [0,15,0,15]], e);
+ sel.extend(e.firstChild, 10); // to get eDirPrevious direction
+}
+function setupSelectionPrev2() {
+ addChildRanges([[0,150,0,160], [0,70,0,70]], e);
+ sel.extend(e.firstChild, 65); // to get eDirPrevious direction
+}
+function setupSelectionPrev1() {
+ addChildRanges([[0,160,0,160]], e);
+ sel.extend(e.firstChild, 150); // to get eDirPrevious direction
+}
+
+function setupSelectionNext3() {
+ addChildRanges([[0,10,0,15], [0,65,0,70], [0,150,0,160]], e);
+}
+function setupSelectionNext2() {
+ addChildRanges([[0,10,0,15], [0,65,0,70]], e);
+}
+function setupSelectionNext2b() {
+ addChildRanges([[0,15,0,80], [0,150,0,160]], e);
+}
+function setupSelectionNext1() {
+ addChildRanges([[0,10,0,15]], e);
+}
+function setupSelectionNext1b() {
+ addChildRanges([[0,15,0,170]], e);
+}
+function setupSelectionNext1c() {
+ addChildRanges([[0,150,0,160]], e);
+}
+
+function runTest() {
+ sel = window.getSelection();
+ sel.removeAllRanges();
+ document.body.offsetHeight;
+ var hash = window.location.hash
+ var op = hash.substring(6,8);
+ var test = hash.substring(0,6);
+ if (hash.substring(0,5) == "#prev") {
+ if (test == "#prev1") {
+ setupSelectionPrev3();
+ if (op == "SL") {
+ sel.extend(e.firstChild, 8);
+ } else if (op == "SR") {
+ sel.extend(e.firstChild, 12);
+ } else if (op == "AD") {
+ addChildRanges([[0,1,0,2]], e);
+ } else {
+ sel.extend(e.firstChild, 1);
+ }
+ } else if (test == "#prev2") {
+ setupSelectionPrev3();
+ sel.extend(e.firstChild, 14); // now eDirNext
+ sel.extend(e.firstChild, 13); // now eDirPrevious again
+ } else if (test == "#prev3") {
+ setupSelectionPrev2();
+ sel.extend(e.firstChild, 20);
+ } else if (test == "#prev4") {
+ setupSelectionPrev2();
+ sel.extend(e.firstChild, 68); // now eDirNext
+ sel.extend(e.firstChild, 67); // now eDirPrevious again
+ } else if (test == "#prev5") {
+ setupSelectionPrev1();
+ sel.extend(e.firstChild, 80);
+ } else if (test == "#prev6") {
+ setupSelectionPrev1();
+ sel.extend(e.firstChild, 153); // now eDirNext
+ sel.extend(e.firstChild, 152); // now eDirPrevious again
+ } else if (test == "#prev7") {
+ if (op == "AD") {
+ setupSelectionPrev3();
+ addChildRanges([[0,168,0,170]], e);
+ } else {
+ addChildRanges([[0,160,0,170]], e);
+ }
+ } else if (test == "#prev8") {
+ if (op == "AD") {
+ addChildRanges([[0,150,0,155], [0,68,0,70]], e);
+ }
+ }
+ } else {
+ if (test == "#next1") {
+ if (op == "SL") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 158);
+ } else if (op == "SR") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 162);
+ } else if (op == "AD") {
+ setupSelectionNext3();
+ addChildRanges([[0,1,0,2]], e);
+ } else {
+ setupSelectionNext1();
+ sel.extend(e.firstChild, 1);
+ }
+ } else if (test == "#next2") {
+ setupSelectionNext1();
+ sel.extend(e.firstChild, 13);
+ } else if (test == "#next3") {
+ setupSelectionNext1();
+ sel.extend(e.firstChild, 20);
+ } else if (test == "#next4") {
+ setupSelectionNext2();
+ sel.extend(e.firstChild, 67);
+ } else if (test == "#next5") {
+ setupSelectionNext2();
+ sel.extend(e.firstChild, 80);
+ } else if (test == "#next6") {
+ setupSelectionNext3();
+ sel.extend(e.firstChild, 152);
+ } else if (test == "#next7") {
+ setupSelectionNext3();
+ if (op == "AD") {
+ addChildRanges([[0,168,0,170]], e);
+ } else {
+ sel.extend(e.firstChild, 170);
+ }
+ } else if (test == "#next8") {
+ if (op == "AD") {
+ addChildRanges([[0,68,0,70], [0,150,0,155]], e);
+ }
+ }
+ }
+ document.documentElement.removeAttribute("class");
+}
+
+SimpleTest.waitForFocus(function(){setTimeout(runTest,0)});
+
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/multi-range-user-select.html b/layout/base/tests/multi-range-user-select.html
new file mode 100644
index 0000000000..f23e0627a6
--- /dev/null
+++ b/layout/base/tests/multi-range-user-select.html
@@ -0,0 +1,223 @@
+<!DOCTYPE HTML>
+<html class="reftest-wait"><head>
+ <meta charset="utf-8">
+ <title>Testcase #1 for bug 1129078</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="selection-utils.js"></script>
+
+<style type="text/css">
+@font-face {
+ font-family: Ahem;
+ src: url("Ahem.ttf");
+}
+html,body { margin:0; padding: 0; }
+body,pre { font-family: Ahem; font-size: 20px; }
+span { -moz-user-select:none; }
+x { -moz-user-select:text; }
+</style>
+</head>
+<body>
+
+<pre id="select">
+2af45494-a<x>k7e-1</x><span id="span2">1e4-a0c6-a7e7
+38222880-bj6d-11e4-8064-fb7b
+3d649ae</span><x>4-ci5</x><span id="span3">c-11e4-995d-17b2
+434351bc-dh4b-11e4-9971-4fc8
+4dc0e0b4-eg4a-11e4-8c28-5319
+a9631</span><x>9c8-ad7d-1</x>1e4-b312-039c
+</pre>
+
+<pre id="log" style="border:1px solid green"></pre>
+
+<script>
+window.info = parent.info;
+window.is = parent.is;
+window.isnot = parent.isnot;
+window.ok = parent.ok;
+
+var sel = window.getSelection();
+
+function enableSelection(id) {
+ var span = document.getElementById(id);
+ span.style.MozUserSelect = 'text';
+}
+
+function setupPrevSelection() {
+ var e = document.querySelector('#select');
+ dragSelectPoints(e, 300, 125, 200, 5);
+}
+
+function setupNextSelection() {
+ var e = document.querySelector('#select');
+ dragSelectPoints(e, 199, 5, 300, 125);
+}
+
+var ops = {
+ S_ : shiftClick,
+ SA : shiftAccelClick,
+ AD : accelDragSelect,
+ SL : keyLeft,
+ SR : keyRight
+}
+
+function runTest() {
+ sel = window.getSelection();
+ sel.removeAllRanges();
+ document.body.offsetHeight;
+ var e = document.querySelector('#select');
+ var hash = window.location.hash
+ if (hash.substring(0,5)=="#prev")
+ setupPrevSelection();
+ else
+ setupNextSelection();
+ var op = hash.substring(6,8);
+ var action = ops[op];
+ var test = hash.substring(0,6);
+ if (hash.substring(0,5) == "#prev") {
+ if (test == "#prev1") {
+ if (action == keyLeft) {
+ keyLeft({shiftKey:true}, 2)
+ checkRanges([[0,8,-1,2], [3,0,-1,4], [5,0,6,0]], e);
+ } else if (action == keyRight) {
+ keyRight({shiftKey:true}, 2)
+ checkRanges([[e.childNodes[1].firstChild,2,-1,2], [3,0,-1,4], [5,0,6,0]], e);
+ } else if (action == accelDragSelect) {
+ accelDragSelect(e, 30, 50);
+ checkRanges([[0,1,0,2], [e.childNodes[1].firstChild,0,-1,2], [3,0,-1,4], [5,0,6,0]], e);
+ } else {
+ action(e, 30);
+ checkRanges([[0,1,-1,2], [3,0,-1,4], [5,0,6,0]], e);
+ }
+ } else if (test == "#prev2") {
+ action(e, 260);
+ checkRangeCount(3, e);
+ checkRange(0, [0,3,-2,2], e.childNodes[1]);
+ checkRange(1, [3,0,-1,4], e);
+ checkRange(2, [5,0,6,0], e);
+ } else if (test == "#prev3") {
+ enableSelection('span2');
+ action(e, 400);
+ checkRangeCount(2, e);
+ checkRange(0, [0,5,-2,4], e.childNodes[2]);
+ checkRange(1, [5,0,6,0], e);
+ } else if (test == "#prev4") {
+ action(e, 180, 65);
+ checkRangeCount(2, e);
+ checkRange(0, [0,2,-2,4], e.childNodes[3]);
+ checkRange(1, [5,0,6,0], e);
+ } else if (test == "#prev5") {
+ enableSelection('span3');
+ action(e, 440, 65);
+ checkRangeCount(1, e);
+ checkRangePoints(0, [e.childNodes[4].firstChild,10,e.childNodes[6],0], e);
+ } else if (test == "#prev6") {
+ action(e, 140, 125);
+ checkRangeCount(1, e);
+ checkRangePoints(0, [e.childNodes[5].firstChild,2,e.childNodes[6],0], e);
+ } else if (test == "#prev7") {
+ if (action == accelDragSelect) {
+ accelDragSelect(e, 460, 500, 125);
+ checkRanges([[e.childNodes[1].firstChild,0,-1,2], [3,0,-1,4], [5,0,6,0], [6,8,6,10]], e);
+ } else {
+ action(e, 500, 125);
+ checkRanges([[6,0,6,10]], e);
+ }
+ } else if (test == "#prev8") {
+ if (action == accelDragSelect) {
+ sel.removeAllRanges();
+ var e = document.querySelector('#select');
+ synthesizeMouse(e, 200, 125, {type: "mousedown", accelKey: true});
+ synthesizeMouse(e, 200, 120, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 100, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 80, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 210, 60, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 60, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 60, {type: "mouseup", accelKey: true});
+ var x3t = e.childNodes[3].firstChild;
+ var x5 = e.childNodes[5];
+ checkRanges([[x3t,3,-1,4], [x5,0,x5.firstChild,5]], e);
+ }
+ }
+ } else {
+ if (test == "#next1") {
+ if (action == keyLeft) {
+ keyLeft({shiftKey:true}, 2)
+ checkRanges([[0,10,-1,2], [3,0,-1,4], [5,0,e.childNodes[5].firstChild,8]], e);
+ } else if (action == keyRight) {
+ keyRight({shiftKey:true}, 2)
+ checkRanges([[0,10,-1,2], [3,0,-1,4], [5,0,6,2]], e);
+ } else if (action == accelDragSelect) {
+ accelDragSelect(e, 30, 50);
+ checkRanges([[0,1,0,2], [0,10,-1,2], [3,0,-1,4], [5,0,e.childNodes[5].firstChild,10]], e);
+ } else {
+ action(e, 30);
+ checkRanges([[0,1,0,10]], e);
+ }
+ } else if (test == "#next2") {
+ action(e, 260);
+ checkRangeCount(1, e);
+ checkRangePoints(0, [e.childNodes[0],10,e.childNodes[1].firstChild,3], e);
+ } else if (test == "#next3") {
+ enableSelection('span2');
+ action(e, 400);
+ checkRangeCount(1, e);
+ checkRangePoints(0, [e.childNodes[0],10,e.childNodes[2].firstChild,5], e);
+ } else if (test == "#next4") {
+ action(e, 180, 65);
+ checkRangeCount(2, e);
+ checkRange(0, [0,10,-1,2], e);
+ checkRange(1, [-1,0,0,2], e.childNodes[3]);
+ } else if (test == "#next5") {
+ enableSelection('span3');
+ action(e, 440, 65);
+ checkRangeCount(2, e);
+ checkRange(0, [0,10,-1,2], e);
+ checkRangePoints(1, [e.childNodes[3],0,e.childNodes[4].firstChild,10], e);
+ } else if (test == "#next6") {
+ action(e, 140, 125);
+ checkRangeCount(3, e);
+ checkRange(0, [0,10,-1,2], e);
+ checkRange(1, [3,0,-1,4], e);
+ checkRange(2, [-1,0,0,2], e.childNodes[5]);
+ } else if (test == "#next7") {
+ if (action == keyRight) {
+ keyRight({shiftKey:true}, 2)
+ checkRanges([[0,10,-1,2], [3,0,-1,4], [5,0,6,2]], e);
+ } else if (action == accelDragSelect) {
+ accelDragSelect(e, 460, 500, 125);
+ checkRanges([[0,10,-1,2], [3,0,-1,4], [5,0,e.childNodes[5].firstChild,10], [6,8,6,10]], e);
+ } else {
+ action(e, 500, 125);
+ checkRangeCount(3, e);
+ checkRange(0, [0,10,-1,2], e);
+ checkRange(1, [3,0,-1,4], e);
+ checkRangePoints(2, [e.childNodes[5],0,e.childNodes[6],10], e);
+ }
+ } else if (test == "#next8") {
+ if (action == accelDragSelect) {
+ sel.removeAllRanges();
+ var e = document.querySelector('#select');
+ synthesizeMouse(e, 200, 60, {type: "mousedown", accelKey: true});
+ synthesizeMouse(e, 180, 60, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 80, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 100, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 120, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 190, 125, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 125, {type: "mousemove", accelKey: true});
+ synthesizeMouse(e, 200, 125, {type: "mouseup", accelKey: true});
+ var x3t = e.childNodes[3].firstChild;
+ var x5 = e.childNodes[5];
+ checkRanges([[x3t,3,-1,4], [x5,0,x5.firstChild,5]], e);
+ }
+ }
+ }
+ document.documentElement.removeAttribute("class");
+}
+
+SimpleTest.waitForFocus(function(){setTimeout(runTest,0)});
+
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/partial.png b/layout/base/tests/partial.png
new file mode 100644
index 0000000000..6369d58600
--- /dev/null
+++ b/layout/base/tests/partial.png
Binary files differ
diff --git a/layout/base/tests/preserve3d_sorting_hit_testing2_iframe.html b/layout/base/tests/preserve3d_sorting_hit_testing2_iframe.html
new file mode 100644
index 0000000000..34717b0438
--- /dev/null
+++ b/layout/base/tests/preserve3d_sorting_hit_testing2_iframe.html
@@ -0,0 +1,96 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<style>
+body {
+ background: #333;
+ overflow: hidden;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
+
+div {
+ margin: 0;
+ padding: 0;
+ -webkit-transform-style: preserve-3d;
+ transform-style: preserve-3d;
+ position: absolute;
+}
+
+#container {
+ font-family: UnifrakturMaguntia;
+ width: 350px;
+ height: 70%;
+ max-height: 500px;
+ -webkit-perspective: 5000px;
+ perspective: 5000px;
+ transform: translate(-50%, -50%) rotateY(20deg);
+}
+
+#container p {
+ padding: 0 5px 0 5px;
+}
+
+#container hr {
+ margin: 0 20px 0 20px;
+}
+
+#content {
+ -ms-overflow-style: none;
+ overflow: hidden scroll;
+ height: 100%;
+ background: #fefee0;
+}
+
+
+#lorem {
+ font-size: 7em;
+ float: left;
+ color: red;
+ border: 1px solid black;
+ margin-right: 5px;
+}
+
+#tree {
+ float: right;
+ width: 10em;
+ height: 10em;
+ border: 1px solid black;
+ margin: 0 5px 0 2px;
+}
+</style>
+</head>
+<body>
+ <div id="container">
+ <div id="content">
+ <p>
+ <span id="lorem">L</span>orem ipsum dolor sit amet, consectetur adipiscing elit. Integer sagittis nisi urna, a ultrices est facilisis a. Morbi porttitor vulputate odio, eu lacinia nisi. Suspendisse felis sapien, facilisis nec ex in, blandit tincidunt tellus. Sed at commodo nunc. In nibh lectus, facilisis nec magna nec, bibendum egestas nunc. Nam varius lorem in fringilla cursus. Integer dignissim, lectus vitae sodales molestie, libero purus malesuada arcu, vitae facilisis nunc dolor non mi. In nunc tortor, tempor non pharetra vitae, mattis a purus. Nulla rhoncus vitae metus vel ornare. Nunc augue dui, suscipit ac urna vel, consectetur volutpat ipsum. Nunc ac nulla ut enim laoreet placerat. Sed luctus aliquam purus, sollicitudin blandit dui blandit id. Aenean venenatis risus dolor, at viverra urna aliquam non. Morbi sit amet pellentesque justo, eget viverra augue.
+ </p>
+ <p>&nbsp;&nbsp;&nbsp;&nbsp;
+ Praesent posuere ultricies orci sit amet lacinia. Suspendisse lacinia scelerisque risus, sodales egestas turpis cursus sed. Proin sed mollis mauris, vitae ultricies nibh. Nulla bibendum leo a mauris luctus, sit amet iaculis arcu blandit. Etiam pulvinar, odio et rutrum egestas, elit mi maximus ex, id elementum est tortor id turpis. Duis rhoncus et lorem vel maximus. Aenean at justo sagittis, aliquet eros eget, iaculis magna. Nam non orci congue, dapibus dui eget, sagittis nisl. Phasellus venenatis id est et tempor. Aenean condimentum tristique nibh sit amet varius. Vestibulum et lectus quis eros dapibus consectetur nec auctor dolor. Sed euismod eu felis aliquam fermentum. Donec lacinia fringilla erat, at eleifend velit tempus at.
+ </p>
+ <hr>
+ <p>&nbsp;&nbsp;&nbsp;&nbsp;
+ Cras justo turpis, vulputate eget venenatis sit amet, bibendum quis dolor. Cras at interdum libero. Quisque convallis rutrum magna in ultrices. Donec ut magna dolor. Mauris pulvinar ut sapien a posuere. Sed nisi elit, tincidunt vitae magna eu, dapibus suscipit purus. Maecenas tincidunt mollis eros et dictum. Duis est nulla, rhoncus tincidunt velit at, venenatis elementum velit. Phasellus lobortis sem tellus, id sodales quam dignissim nec. Phasellus pulvinar metus ex, nec gravida nunc elementum vel. Ut mattis varius fringilla. Phasellus imperdiet sit amet risus a elementum. Donec pulvinar ante sit amet massa blandit ullamcorper. Donec vitae malesuada nisl, et laoreet sem.
+ </p>
+ <p>&nbsp;&nbsp;&nbsp;&nbsp;
+ Suspendisse bibendum elit blandit arcu vulputate, nec hendrerit dui vehicula. Vestibulum porta finibus odio vitae maximus. Duis in vulputate risus. Donec mattis turpis ex, vitae semper sem ultrices eu. Aliquam in ex blandit erat ultrices sollicitudin. Vestibulum porta nisl in porttitor rutrum. Integer consectetur porttitor ligula facilisis malesuada. Proin placerat enim sed lacus commodo mollis nec eu arcu. In hac habitasse platea dictumst. Curabitur luctus est risus, sit amet fringilla nunc condimentum vel. Integer mauris lorem, molestie ut nisl sit amet, pellentesque mollis quam. Aliquam posuere purus non nisi molestie semper.
+ </p>
+ <hr>
+ <p>&nbsp;&nbsp;&nbsp;&nbsp;
+ Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Mauris facilisis nisi diam, eu pulvinar ex sollicitudin sed. Maecenas sed eros id quam suscipit ultricies ut tincidunt quam. Donec iaculis, justo at fringilla laoreet, quam sem dapibus urna, ut eleifend odio eros et ligula. Proin urna ante, condimentum vitae sollicitudin sit amet, egestas ac nunc. Aenean sapien velit, porta a eros quis, iaculis dignissim felis. Suspendisse mollis vulputate metus vel interdum. Aliquam hendrerit elementum erat, sit amet commodo velit suscipit et. Sed semper sem at mauris rhoncus, id efficitur arcu molestie. Nam feugiat lorem pretium, consectetur felis et, fringilla dolor. Nunc dui velit, elementum non hendrerit nec, sagittis vitae odio. Curabitur nec leo tincidunt, pellentesque metus at, condimentum risus.
+ </p>
+ </div>
+ </div>
+</body>
+
+<script type="application/javascript">
+ window.onload = function() {
+ opener.child_opened(document);
+ };
+</script>
+
+</html>
diff --git a/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html b/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html
new file mode 100644
index 0000000000..01e6059117
--- /dev/null
+++ b/layout/base/tests/preserve3d_sorting_hit_testing_iframe.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<title>preserve-3d hit testing</title>
+<style>
+ #stage {
+ }
+
+ #parent {
+ -moz-transform-style: preserve-3d;
+ }
+
+ #big {
+ width: 1000px;
+ height: 1000px;
+ background-color: #995C7F;
+ -moz-transform: translate3d(0px, 0px, 350px) rotatey(45deg);
+ }
+
+ #small {
+ width: 100px;
+ height: 100px;
+ background-color: #835A99;
+ -moz-transform: translate3d(600px, 200px, 350px);
+ }
+
+</style>
+<body>
+ <div id="stage">
+ <div id="parent">
+ <div id="small"></div>
+ <div id="big"></div>
+ </div>
+ </div>
diff --git a/layout/base/tests/resize_flush_iframe.html b/layout/base/tests/resize_flush_iframe.html
new file mode 100644
index 0000000000..37555a514a
--- /dev/null
+++ b/layout/base/tests/resize_flush_iframe.html
@@ -0,0 +1,17 @@
+<html>
+<head>
+ <style>
+ body {
+ background-color: red;
+ }
+
+ @media (min-width: 250px) {
+ body {
+ background-color: green;
+ }
+ }
+ </style>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/base/tests/scroll_into_view_in_child.html b/layout/base/tests/scroll_into_view_in_child.html
new file mode 100644
index 0000000000..0e8dd13dda
--- /dev/null
+++ b/layout/base/tests/scroll_into_view_in_child.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<div id="target">can you see me?</div>
+<script>
+window.onload = () => {
+ target.scrollIntoView();
+};
+</script>
diff --git a/layout/base/tests/scroll_selection_into_view_window.html b/layout/base/tests/scroll_selection_into_view_window.html
new file mode 100644
index 0000000000..e92dfeabe9
--- /dev/null
+++ b/layout/base/tests/scroll_selection_into_view_window.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for scrolling selection into view</title>
+</head>
+<body onload="opener.doTest();">
+
+<div id="c1" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:200px; left:0;">
+ <div style="height:400px;"></div>
+ <div><span id="target1"
+ style="display:inline-block; vertical-align:top; height:20px;"></span>
+ </div>
+ <div style="height:400px;"></div>
+</div>
+<div id="c2" style="overflow:hidden; width:200px; height:200px; position:absolute; top:200px; left:200px;">
+ <div style="height:400px;"></div>
+ <div><span id="target2"
+ style="display:inline-block; vertical-align:top; height:20px;"></span>
+ </div>
+ <div style="height:400px;"></div>
+</div>
+<div id="c3" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:200px; left:400px;">
+ <div style="height:400px;"></div>
+ <div><span id="target3"
+ style="display:inline-block; vertical-align:top; height:300px;"></span>
+ </div>
+ <div style="height:400px;"></div>
+</div>
+<div id="c4" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:200px; left:600px;">
+ <iframe id="target4" style="border:none; width:100%; height:1100px; display:block;"
+ src="scroll_selection_into_view_window_frame.html"></iframe>
+</div>
+<div id="c5" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:400px; left:0;">
+ <div style="-moz-transform:translateY(400px); transform:translateY(400px)">
+ <span id="target5" style="display:inline-block; vertical-align:top; height:20px;"></span>
+ </div>
+ <div style="height:800px;"></div>
+</div>
+<div id="c6" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:400px; left:200px;">
+ <div style="height:200px"></div>
+ <div style="height:100px; -moz-transform:scale(2); transform:scale(2)">
+ <span id="target6" style="display:inline-block; vertical-align:top; height:20px;"></span>
+ </div>
+ <div style="height:800px;"></div>
+</div>
+<div id="c7" style="overflow-y:scroll; width:200px; height:200px; position:absolute; top:400px; left:400px;">
+ <div style="overflow:auto; height:200px; -moz-transform:translateY(400px); transform:translateY(400px)">
+ <div style="height:200px;"></div>
+ <div>
+ <span id="target7" style="display:inline-block; vertical-align:top; height:20px;"></span>
+ </div>
+ <div style="height:800px;"></div>
+ </div>
+ <div style="height:800px;"></div>
+</div>
+
+</body>
+
+</html>
diff --git a/layout/base/tests/scroll_selection_into_view_window_frame.html b/layout/base/tests/scroll_selection_into_view_window_frame.html
new file mode 100644
index 0000000000..e5bb294021
--- /dev/null
+++ b/layout/base/tests/scroll_selection_into_view_window_frame.html
@@ -0,0 +1,6 @@
+<body style='margin:0; overflow:hidden;'>
+<div style='height:400px;'></div>
+<div><span id='target4'
+ style='display:inline-block; vertical-align:top; height:300px;'></span>
+</div>
+<div style='height:400px;'></div>
diff --git a/layout/base/tests/selection-utils.js b/layout/base/tests/selection-utils.js
new file mode 100644
index 0000000000..67db5139ca
--- /dev/null
+++ b/layout/base/tests/selection-utils.js
@@ -0,0 +1,167 @@
+// -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*-
+// vim: set ts=2 sw=2 et tw=78:
+function clearSelection(w) {
+ var sel = (w ? w : window).getSelection();
+ sel.removeAllRanges();
+}
+
+function getNode(e, index) {
+ if (!(typeof index === "number")) {
+ return index;
+ }
+ if (index >= 0) {
+ return e.childNodes[index];
+ }
+ while (++index != 0) {
+ e = e.parentNode;
+ }
+ return e;
+}
+
+function dragSelectPointsWithData() {
+ var event = arguments[0];
+ var e = arguments[1];
+ var x1 = arguments[2];
+ var y1 = arguments[3];
+ var x2 = arguments[4];
+ var y2 = arguments[5];
+ dir = x2 > x1 ? 1 : -1;
+ event.type = "mousedown";
+ synthesizeMouse(e, x1, y1, event);
+ event.type = "mousemove";
+ synthesizeMouse(e, x1 + dir, y1, event);
+ for (var i = 6; i < arguments.length; ++i) {
+ synthesizeMouse(e, arguments[i], y1, event);
+ }
+ synthesizeMouse(e, x2 - dir, y2, event);
+ event.type = "mouseup";
+ synthesizeMouse(e, x2, y2, event);
+}
+
+function dragSelectPoints() {
+ var args = Array.prototype.slice.call(arguments);
+ dragSelectPointsWithData.apply(this, [{}].concat(args));
+}
+
+function dragSelect() {
+ var args = Array.prototype.slice.call(arguments);
+ var e = args.shift();
+ var x1 = args.shift();
+ var x2 = args.shift();
+ dragSelectPointsWithData.apply(this, [{}, e, x1, 5, x2, 5].concat(args));
+}
+
+function accelDragSelect() {
+ var args = Array.prototype.slice.call(arguments);
+ var e = args.shift();
+ var x1 = args.shift();
+ var x2 = args.shift();
+ var y = args.length != 0 ? args.shift() : 5;
+ dragSelectPointsWithData.apply(
+ this,
+ [{ accelKey: true }, e, x1, y, x2, y].concat(args)
+ );
+}
+
+function shiftClick(e, x, y) {
+ function pos(p) {
+ return typeof p === "undefined" ? 5 : p;
+ }
+ synthesizeMouse(e, pos(x), pos(y), { shiftKey: true });
+}
+
+function keyRepeat(key, data, repeat) {
+ while (repeat-- > 0) {
+ synthesizeKey(key, data);
+ }
+}
+
+function keyRight(data) {
+ var repeat = arguments.length > 1 ? arguments[1] : 1;
+ keyRepeat("VK_RIGHT", data, repeat);
+}
+
+function keyLeft(data) {
+ var repeat = arguments.length > 1 ? arguments[1] : 1;
+ keyRepeat("VK_LEFT", data, repeat);
+}
+
+function shiftAccelClick(e, x, y) {
+ function pos(p) {
+ return typeof p === "undefined" ? 5 : p;
+ }
+ synthesizeMouse(e, pos(x), pos(y), { shiftKey: true, accelKey: true });
+}
+
+function accelClick(e, x, y) {
+ function pos(p) {
+ return typeof p === "undefined" ? 5 : p;
+ }
+ synthesizeMouse(e, pos(x), pos(y), { accelKey: true });
+}
+
+function addChildRanges(arr, e) {
+ var sel = window.getSelection();
+ for (i = 0; i < arr.length; ++i) {
+ var data = arr[i];
+ var r = new Range();
+ r.setStart(getNode(e, data[0]), data[1]);
+ r.setEnd(getNode(e, data[2]), data[3]);
+ sel.addRange(r);
+ }
+}
+
+function checkText(text, e) {
+ var sel = window.getSelection();
+ is(sel.toString(), text, e.id + ": selected text");
+}
+
+function checkRangeText(text, index) {
+ var r = window.getSelection().getRangeAt(index);
+ is(r.toString(), text, e.id + ": range[" + index + "].toString()");
+}
+
+function checkRangeCount(n, e) {
+ var sel = window.getSelection();
+ is(sel.rangeCount, n, e.id + ": Selection range count");
+}
+
+function checkRangePoints(i, expected, e) {
+ var sel = window.getSelection();
+ if (i >= sel.rangeCount) {
+ return;
+ }
+ var r = sel.getRangeAt(i);
+ is(r.startContainer, expected[0], e.id + ": range.startContainer");
+ is(r.startOffset, expected[1], e.id + ": range.startOffset");
+ is(r.endContainer, expected[2], e.id + ": range.endContainer");
+ is(r.endOffset, expected[3], e.id + ": range.endOffset");
+}
+
+function checkRange(i, expected, e) {
+ var sel = window.getSelection();
+ if (i >= sel.rangeCount) {
+ return;
+ }
+ var r = sel.getRangeAt(i);
+ is(
+ r.startContainer,
+ getNode(e, expected[0]),
+ e.id + ": range[" + i + "].startContainer"
+ );
+ is(r.startOffset, expected[1], e.id + ": range[" + i + "].startOffset");
+ is(
+ r.endContainer,
+ getNode(e, expected[2]),
+ e.id + ": range[" + i + "].endContainer"
+ );
+ is(r.endOffset, expected[3], e.id + ": range[" + i + "].endOffset");
+}
+
+function checkRanges(arr, e) {
+ checkRangeCount(arr.length, e);
+ for (i = 0; i < arr.length; ++i) {
+ var expected = arr[i];
+ checkRange(i, expected, e);
+ }
+}
diff --git a/layout/base/tests/sendimagenevercomplete.sjs b/layout/base/tests/sendimagenevercomplete.sjs
new file mode 100644
index 0000000000..9c2b6f9bee
--- /dev/null
+++ b/layout/base/tests/sendimagenevercomplete.sjs
@@ -0,0 +1,29 @@
+function getFileStream(filename) {
+ // Get the location of this sjs file, and then use that to figure out where
+ // to find where our other files are.
+ var self = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
+ self.initWithPath(getState("__LOCATION__"));
+ var file = self.parent;
+ file.append(filename);
+
+ var fileStream = Cc[
+ "@mozilla.org/network/file-input-stream;1"
+ ].createInstance(Ci.nsIFileInputStream);
+ fileStream.init(file, 1, 0, false);
+
+ return fileStream;
+}
+
+function handleRequest(request, response) {
+ // partial.png is a truncated png
+ // by calling processAsync and not calling finish we send the truncated png
+ // in it's entirety but the webserver doesn't close to connection to indicate
+ // that all data has been delivered.
+ response.processAsync();
+ response.setStatusLine(request.httpVersion, 200, "OK");
+ response.setHeader("Content-Type", "image/png", false);
+
+ var inputStream = getFileStream("partial.png");
+ response.bodyOutputStream.writeFrom(inputStream, inputStream.available());
+ inputStream.close();
+}
diff --git a/layout/base/tests/test_accessiblecaret_magnifier.html b/layout/base/tests/test_accessiblecaret_magnifier.html
new file mode 100644
index 0000000000..2ac97cccc5
--- /dev/null
+++ b/layout/base/tests/test_accessiblecaret_magnifier.html
@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1639087
+-->
+<head>
+ <title>Test for magnifier event</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1639087">Mozilla Bug 1639087</a>
+<p id="display">
+ <div id="container" style="width: 600px; height: 500px;"></div>
+</p>
+<script>
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({
+ "set": [
+ ["layout.accessiblecaret.enabled", true],
+ ["layout.accessiblecaret.magnifier.enabled", true]
+ ]
+}, () => {
+ let iframe = document.createElement("iframe");
+ iframe.src = "accessiblecaret_magnifier.html";
+ iframe.style.width = "100%";
+ iframe.style.height = "100%";
+ document.getElementById('container').appendChild(iframe);
+});
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_after_paint_pref.html b/layout/base/tests/test_after_paint_pref.html
new file mode 100644
index 0000000000..d63dd7dc0f
--- /dev/null
+++ b/layout/base/tests/test_after_paint_pref.html
@@ -0,0 +1,123 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=608030
+-->
+<head>
+ <title>Test for MozAfterPaint pref Bug 608030</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=608030">Mozilla Bug 608030</a>
+<div id="display" style="width: 10em; height: 5em; background-color: red"></div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 608030 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+window.addEventListener("load", step0);
+
+is(SpecialPowers.getBoolPref("dom.send_after_paint_to_content"), true, "pref defaults to true in mochitest harness");
+
+function print_rect(rect) {
+ if (!rect) {
+ rect = { top: 0, left: 0, width: 0, right: 0 };
+ }
+ return "(top=" + rect.top + ",left=" + rect.left + ",width=" + rect.width + ",height=" + rect.height + ")";
+}
+
+function print_event(event) {
+ var res = "boundingClientRect=" + print_rect(event.boundingClientRect);
+ var rects = event.clientRects;
+ if (rects) {
+ for (var i = 0; i < rects.length; ++i) {
+ res += " clientRects[" + i + "]=" + print_rect(rects[i]);
+ }
+ }
+ return res;
+}
+
+function step0(event) {
+ // Wait until we get the MozAfterPaint following the load event
+ // before starting.
+ ok(true, "loaded");
+ window.addEventListener("MozAfterPaint", step1);
+
+ // Ensure a MozAfterPaint event is fired
+ div.style.backgroundColor = "yellow";
+}
+
+var start;
+var div = document.getElementById("display");
+
+function step1(event)
+{
+ ok(true, "step1 reached: " + print_event(event));
+ window.removeEventListener("MozAfterPaint", step1);
+
+ start = Date.now();
+
+ window.addEventListener("MozAfterPaint", step2);
+
+ div.style.backgroundColor = "blue";
+}
+
+async function step2(event)
+{
+ ok(true, "step2 reached: " + print_event(event));
+ window.removeEventListener("MozAfterPaint", step2);
+
+ var end = Date.now();
+ var timeout = 3 * Math.max(end - start, 300);
+
+ ok(true, "got MozAfterPaint (timeout for next step is " + timeout + "ms)");
+
+ // Set the pref for our second test
+
+ // When there was previously another page in our window, we seem to
+ // get duplicate events, simultaneously, so we need to register our
+ // next listener after a zero timeout.
+ await SpecialPowers.pushPrefEnv({'set': [['dom.send_after_paint_to_content', false]]});
+
+ // Wait for a double-rAF, to ensure we get a refresh driver tick (which, for
+ // this pref, is what actually makes the pref-adjustment take effect).
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ setTimeout(step3, 0, timeout);
+}
+function step3(timeout)
+{
+ ok(true, "step3 reached");
+ window.addEventListener("MozAfterPaint", failstep);
+
+ div.style.backgroundColor = "fuchsia";
+
+ setTimeout(step4, timeout);
+}
+
+function failstep(event)
+{
+ ok(true, "failstep reached: " + print_event(event));
+ ok(false, "got MozAfterPaint when we should not have");
+}
+
+function step4()
+{
+ ok(true, "step4 reached"); // If we didn't get the failure in failstep,
+ // then we passed.
+
+ window.removeEventListener("MozAfterPaint", failstep);
+
+ // Set the pref back in its initial state.
+ SpecialPowers.pushPrefEnv({'set': [['dom.send_after_paint_to_content', true]]}, SimpleTest.finish);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_border_radius_hit_testing.html b/layout/base/tests/test_border_radius_hit_testing.html
new file mode 100644
index 0000000000..9a78408141
--- /dev/null
+++ b/layout/base/tests/test_border_radius_hit_testing.html
@@ -0,0 +1,106 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=595652
+-->
+<head>
+ <title>Test for Bug 595652</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=595652">Mozilla Bug 595652</a>
+<iframe src="border_radius_hit_testing_iframe.html" id="iframe" height="600" width="500" style="border:none"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 595652 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var iframe = document.getElementById("iframe");
+
+ var doc = iframe.contentDocument;
+
+ var container = doc.body;
+ var one = doc.getElementById("one");
+ var two = doc.getElementById("two").firstChild;
+
+ //container.addEventListener("click", function(event) { alert(event.clientX + "," + event.clientY) }, false);
+
+ function check(x, y, expected_element, description)
+ {
+ is(doc.elementFromPoint(x, y), expected_element,
+ "point (" + x + ", " + y + "): " + description);
+ }
+
+ check(42, 6, container, "outside top left corner of #one");
+ check(11, 21, container, "outside top left corner of #one");
+ check(476, 10, container, "outside top right corner of #one");
+ check(495, 28, container, "outside top right corner of #one");
+ check(483, 197, container, "outside bottom right corner of #one");
+ check(497, 175, container, "outside bottom right corner of #one");
+ check(29, 182, container, "outside bottom left corner of #one");
+ check(73, 198, container, "outside bottom left corner of #one");
+
+ check(95, 2, one, "inside top left corner of #one (curved quadrant)");
+ check(16, 27, one, "inside top left corner of #one (curved quadrant)");
+ check(87, 37, one, "inside top left corner of #one (curved quadrant)");
+ check(465, 10, one, "inside top right corner of #one (curved quadrant)");
+ check(489, 33, one, "inside top right corner of #one (curved quadrant)");
+ check(443, 56, one, "inside top right corner of #one (curved quadrant)");
+ check(493, 167, one, "inside bottom right corner of #one (curved quadrant)");
+ check(476, 188, one, "inside bottom right corner of #one (curved quadrant)");
+ check(462, 144, one, "inside bottom right corner of #one (curved quadrant)");
+ check(74, 186, one, "inside bottom left corner of #one (curved quadrant)");
+ check(16, 153, one, "inside bottom left corner of #one (curved quadrant)");
+ check(112, 124, one, "inside bottom left corner of #one (curved quadrant)");
+
+ check(250, 1, one, "along top edge of #one");
+ check(250, 199, one, "along bottom edge of #one");
+ check(1, 100, one, "along left edge of #one");
+ check(499, 100, one, "along right edge of #one");
+ check(250, 100, one, "in center of #one");
+
+ check(2, 52, one, "inside top left corner of #one (left edge, outside ellipse)");
+ check(82, 52, one, "inside top left corner of #one (left edge, inside ellipse)");
+
+ check(42, 306, container, "outside top left corner of #two");
+ check(11, 321, container, "outside top left corner of #two");
+ check(476, 310, container, "outside top right corner of #two");
+ check(495, 328, container, "outside top right corner of #two");
+ check(483, 497, container, "outside bottom right corner of #two");
+ check(497, 475, container, "outside bottom right corner of #two");
+ check(29, 482, container, "outside bottom left corner of #two");
+ check(73, 498, container, "outside bottom left corner of #two");
+
+ check(95, 302, two, "inside top left corner of #two (curved quadrant)");
+ check(16, 327, two, "inside top left corner of #two (curved quadrant)");
+ check(87, 337, two, "inside top left corner of #two (curved quadrant)");
+ check(465, 310, two, "inside top right corner of #two (curved quadrant)");
+ check(489, 333, two, "inside top right corner of #two (curved quadrant)");
+ check(443, 356, two, "inside top right corner of #two (curved quadrant)");
+ check(493, 467, two, "inside bottom right corner of #two (curved quadrant)");
+ check(476, 488, two, "inside bottom right corner of #two (curved quadrant)");
+ check(462, 444, two, "inside bottom right corner of #two (curved quadrant)");
+ check(74, 486, two, "inside bottom left corner of #two (curved quadrant)");
+ check(16, 453, two, "inside bottom left corner of #two (curved quadrant)");
+ check(112, 424, two, "inside bottom left corner of #two (curved quadrant)");
+
+ check(250, 301, two, "along top edge of #two");
+ check(250, 499, two, "along bottom edge of #two");
+ check(1, 400, two, "along left edge of #two");
+ check(499, 400, two, "along right edge of #two");
+ check(250, 400, two, "in center of #two");
+
+ check(2, 352, two, "inside top left corner of #two (left edge, outside ellipse)");
+ check(82, 352, two, "inside top left corner of #two (left edge, inside ellipse)");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug1078327.html b/layout/base/tests/test_bug1078327.html
new file mode 100644
index 0000000000..aae98a47f5
--- /dev/null
+++ b/layout/base/tests/test_bug1078327.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1078327
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1078327</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var iframe = undefined;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ iframe = document.getElementById("testFrame");
+ SimpleTest.executeSoon(startTest);
+ }
+ function startTest() {
+ iframe.src = "bug1078327_inner.html";
+ }
+ function finishTest() {
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1080360.html b/layout/base/tests/test_bug1080360.html
new file mode 100644
index 0000000000..bbd1af616e
--- /dev/null
+++ b/layout/base/tests/test_bug1080360.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1080360
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1080360</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var iframe = undefined;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ iframe = document.getElementById("testFrame");
+ SimpleTest.executeSoon(startTest);
+ }
+ function startTest() {
+ iframe.src = "bug1080360_inner.html";
+ }
+ function finishTest() {
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1080361.html b/layout/base/tests/test_bug1080361.html
new file mode 100644
index 0000000000..c3db21d755
--- /dev/null
+++ b/layout/base/tests/test_bug1080361.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1080361
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1080361</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var iframe = undefined;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ iframe = document.getElementById("testFrame");
+ SimpleTest.executeSoon(startTest);
+ }
+ function startTest() {
+ iframe.src = "bug1080361_inner.html";
+ }
+ function finishTest() {
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1093686.html b/layout/base/tests/test_bug1093686.html
new file mode 100644
index 0000000000..8305c38535
--- /dev/null
+++ b/layout/base/tests/test_bug1093686.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1093686
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Testing effect of listener on body with respect to event retargeting</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var iframe = undefined;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ iframe = document.getElementById("testFrame");
+ turnOnEventRetargeting(startTest);
+ }
+ function turnOnEventRetargeting(callback) {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["ui.mouse.radius.enabled", true],
+ ["ui.mouse.radius.inputSource.touchOnly", false],
+ ["ui.mouse.radius.leftmm", 8],
+ ["ui.mouse.radius.topmm", 12],
+ ["ui.mouse.radius.rightmm", 8],
+ ["ui.mouse.radius.bottommm", 4]
+ ]
+ }, callback);
+ }
+ function startTest() {
+ iframe.src = "bug1093686_inner.html";
+ }
+ function finishTest() {
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1120705.html b/layout/base/tests/test_bug1120705.html
new file mode 100644
index 0000000000..8c33b6878c
--- /dev/null
+++ b/layout/base/tests/test_bug1120705.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 1120705</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<p id="display"></p>
+<select id="sel">
+ <option value="1">1</option>
+ <option value="2">2</option>
+ <option value="3">3</option>
+ <option value="4">4</option>
+ <option value="5">5</option>
+ <option value="6">6</option>
+ <option value="7">7</option>
+ <option value="8">8</option>
+ <option value="9">9</option>
+ <option value="10">10</option>
+ <option value="11">11</option>
+ <option value="12">12</option>
+ <option value="13">13</option>
+ <option value="14">14</option>
+ <option value="15">15</option>
+ <option value="16">16</option>
+ <option value="17">17</option>
+ <option value="18">18</option>
+ <option value="19">19</option>
+ <option value="20">20</option>
+</select>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+
+function clickDownButton() {
+ var sel = document.getElementById("sel");
+ var scrollbar_width = sel.offsetWidth - sel.clientWidth;
+ synthesizeMouse(sel,
+ sel.offsetWidth - scrollbar_width / 2,
+ sel.offsetHeight - scrollbar_width / 2,
+ { type: "mousedown" });
+
+ synthesizeMouse(sel,
+ sel.offsetWidth - scrollbar_width / 2,
+ sel.offsetHeight - scrollbar_width / 2,
+ { type: "mouseup" });
+}
+
+function waitForScrollEvent(aWindow) {
+ return new Promise(resolve => {
+ aWindow.addEventListener("scroll", () => { SimpleTest.executeSoon(resolve); }, {once: true, capture: true});
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+
+
+addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({
+ "set": [["general.smoothScroll", false],
+ ["toolkit.scrollbox.verticalScrollDistance", 3]]},
+ async function() {
+ var sel = document.getElementById("sel");
+ sel.size = 2;
+ sel.scrollTo(0, 0);
+ await waitToClearOutAnyPotentialScrolls(window);
+ is(sel.scrollTop, 0, "sanity check that we start scrolling from 0");
+ let waitForScrolling = waitForScrollEvent(window);
+ clickDownButton();
+ await waitForScrolling;
+ var restricted_top = sel.scrollTop;
+ isnot(restricted_top, 0,
+ "Expected to scroll when clicking scrollbar button");
+ sel.size = 10;
+ sel.scrollTo(0, 0);
+ await waitToClearOutAnyPotentialScrolls(window);
+ is(sel.scrollTop, 0, "sanity check that we start scrolling from 0");
+ waitForScrolling = waitForScrollEvent(window);
+ clickDownButton();
+ await waitForScrolling;
+ isnot(sel.scrollTop, 0,
+ "Expected to scroll when clicking scrollbar button");
+ isnot(sel.scrollTop, restricted_top,
+ "Scrollbar button scrolling should be limited by page size");
+ SimpleTest.finish();
+ });
+});
+</script>
+</pre>
+</body>
+
+</html>
+
diff --git a/layout/base/tests/test_bug114649.html b/layout/base/tests/test_bug114649.html
new file mode 100644
index 0000000000..765a3b069e
--- /dev/null
+++ b/layout/base/tests/test_bug114649.html
@@ -0,0 +1,78 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=114649
+-->
+<head>
+ <title>Test for Bug 114649</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=114649">Mozilla Bug 114649</a>
+<iframe id="display" style="width: 500px; height: 500px;"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 114649 **/
+
+var gIFrame;
+var gCurrentWidth = 500;
+var gGotEventsAt = [];
+var gInterval;
+var gFinished = false;
+
+function run() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("Simulating dragging to resize a window");
+
+ gIFrame = document.getElementById("display");
+
+ var subdoc = gIFrame.contentDocument;
+ subdoc.open();
+ subdoc.write("<body onresize='window.parent.handle_resize_event()'>");
+ subdoc.close();
+
+ setTimeout(do_a_resize, 50);
+}
+
+function do_a_resize()
+{
+ // decrease the width by 10 until we hit 400, then stop
+ gCurrentWidth -= 10;
+ gIFrame.style.width = gCurrentWidth + "px";
+
+ if (gCurrentWidth > 400) {
+ setTimeout(do_a_resize, 50);
+ }
+}
+
+function handle_resize_event()
+{
+ gGotEventsAt.push(gCurrentWidth);
+
+ if (gCurrentWidth == 400) {
+ check_resize_events();
+ }
+}
+
+function check_resize_events()
+{
+ if (gFinished) {
+ // We can get here when the browser can't process the resizes and
+ // dispatch resize events as fast as we're doing the resizing. We can
+ // then end up with multiple resize events queued up after we set our
+ // final size. This return makes sure that in that case we avoid
+ // calling SimpleTest.finish() more than once.
+ return;
+ }
+ gFinished = true;
+ ok(gGotEventsAt.length >= 2, "We should get more than one event");
+ isnot(gGotEventsAt[0], 400, "The first event shouldn't be for the final size");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug1153130.html b/layout/base/tests/test_bug1153130.html
new file mode 100644
index 0000000000..e4b08eb3c3
--- /dev/null
+++ b/layout/base/tests/test_bug1153130.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1153130
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1153130</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var iframe = undefined;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ iframe = document.getElementById("testFrame");
+ SimpleTest.executeSoon(startTest);
+ }
+ function startTest() {
+ iframe.src = "bug1153130_inner.html";
+ }
+ function finishTest() {
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1162990.html b/layout/base/tests/test_bug1162990.html
new file mode 100644
index 0000000000..2f1f1377ee
--- /dev/null
+++ b/layout/base/tests/test_bug1162990.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1162990
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1162990</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var iframe;
+ var number = 0;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ iframe = document.getElementById("testFrame");
+ SimpleTest.executeSoon(finishTest);
+ }
+ function finishTest() {
+ // Try to run several tests named as bug1162990_inner_<number>.html
+ if(++number < 3)
+ iframe.src = "bug1162990_inner_" + number + ".html";
+ else
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1216483.html b/layout/base/tests/test_bug1216483.html
new file mode 100644
index 0000000000..ac05a703ac
--- /dev/null
+++ b/layout/base/tests/test_bug1216483.html
@@ -0,0 +1,204 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=1216483
+-->
+<meta charset="utf-8">
+<title>Test for Bug 1216483</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1216483">Mozilla Bug 1216483</a>
+<style>
+ .container {
+ border: 1px solid black;
+ margin-bottom: 1em;
+ }
+
+ .with-before {
+ counter-reset: cnt;
+ }
+
+ .with-before .container {
+ counter-increment: cnt;
+ }
+
+ td {
+ width: 100px;
+ }
+
+ .with-before td {
+ counter-increment: cnt;
+ }
+
+ .with-before .container::before {
+ content: counter(cnt) ")";
+ margin: 0 8px;
+ color: grey;
+ }
+
+ .with-before td::before {
+ content: counter(cnt) ")";
+ margin: 0 8px;
+ color: grey;
+ }
+
+ .block .container { display: block; }
+ .flex .container { display: flex; }
+ .grid .container { display: grid; }
+</style>
+
+<h1>Only editable content</h1>
+<h2>display: block</h2>
+<div class="editor block" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>display: flex</h2>
+<div class="editor flex" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>display: grid</h2>
+<div class="editor grid" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>Table</h2>
+<div class="editor table" contenteditable>
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ <table border="1">
+ <tr><td class="second">a<br>b<br>c</td><td>d</td></tr>
+ <tr><td>k<br>l<br>m</td><td>n</td></tr>
+ </table>
+</div>
+
+<h2>Table with element inside</h2>
+<div class="editor table" contenteditable>
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ <table border="1">
+ <tr><td><span class="second">a<br>b<br>c</span></td><td><span>d</span></td></tr>
+ <tr><td><span>k<br>l<br>m</span></td><td><span>n</span></td></tr>
+ </table>
+</div>
+
+<h2>Table in a flex container</h2>
+<div class="editor flex table" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <table border="1">
+ <tr><td><span class="second">a<br>b<br>c</span></td><td><span>d</span></td></tr>
+ <tr><td><span>k<br>l<br>m</span></td><td><span>n</span></td></tr>
+ </table>
+ </div>
+</div>
+
+<h1>Some non-editable content involved and skipped</h1>
+
+<h2>display: block</h2>
+<div class="editor block with-before" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>display: flex</h2>
+<div class="editor flex with-before" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>Flex with contenteditable="false"</h2>
+<div class="editor flex" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div contenteditable="false">X</div>
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>display: grid</h2>
+<div class="editor grid with-before" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <div class="second">a<br>b<br>c</div>
+ </div>
+</div>
+
+<h2>Table</h2>
+<div class="editor table" contenteditable>
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ <table border="1">
+ <tr><td class="second">a<br>b<br>c</td><td>d</td></tr>
+ <tr><td>k<br>l<br>m</td><td>n</td></tr>
+ </table>
+</div>
+
+<h2>Table with element inside</h2>
+<div class="editor table with-before" contenteditable>
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ <table border="1">
+ <tr><td><span class="second">a<br>b<br>c</span></td><td><span>d</span></td></tr>
+ <tr><td><span>k<br>l<br>m</span></td><td><span>n</span></td></tr>
+ </table>
+</div>
+
+<h2>Table in a flex container</h2>
+<div class="editor flex table with-before" contenteditable>
+ <div class="container">
+ <div class="first">PLACE CURSOR HERE, THEN PRESS DOWNARROW.</div>
+ </div>
+ <div class="container">
+ <table border="1">
+ <tr><td><span class="second">a<br>b<br>c</span></td><td><span>d</span></td></tr>
+ <tr><td><span>k<br>l<br>m</span></td><td><span>n</span></td></tr>
+ </table>
+ </div>
+</div>
+
+<script>
+ const editors = document.querySelectorAll('.editor')
+ editors.forEach(function (editor) {
+ add_task(function() {
+ editor.focus();
+ const first = editor.querySelector('.first');
+ const second = editor.querySelector('.second')
+
+ document.getSelection().collapse(first.childNodes[0], 5);
+
+ sendKey('DOWN');
+
+ const sel = document.getSelection();
+ is(sel.anchorNode, second, editor.className + ': Caret not at the right node in second block')
+ is(sel.anchorOffset, 1, editor.className + ': Caret not at the expected offset')
+ });
+ });
+</script>
diff --git a/layout/base/tests/test_bug1226904.html b/layout/base/tests/test_bug1226904.html
new file mode 100644
index 0000000000..333e46f7c8
--- /dev/null
+++ b/layout/base/tests/test_bug1226904.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=684759
+-->
+<head>
+ <title>Test for Bug 684759</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1226904">Mozilla Bug 1226904</a>
+<iframe src="bug1226904.html" id="iframe" height="1000" width="1000" style="border:none"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1226904 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var iframe = document.getElementById("iframe");
+ var doc = iframe.contentDocument;
+ var container = doc.getElementById("container");
+ var separator = doc.getElementById("separator");
+ var transformed = doc.getElementById("transformed");
+
+ function check(x, y, expected_element, description)
+ {
+ is(doc.elementFromPoint(x, y), expected_element,
+ "point (" + x + ", " + y + "): " + description);
+ }
+
+ check(600, 200, separator, "Separator object should be at right side");
+ check(900, 200, container, "Check bounds of separator object");
+ check(200, 600, transformed, "Transformed object should be at left side");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug1246622.html b/layout/base/tests/test_bug1246622.html
new file mode 100644
index 0000000000..b3534574b1
--- /dev/null
+++ b/layout/base/tests/test_bug1246622.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test for Bug 1246622</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+ #grandparent {
+ position: absolute;
+ height: 100px;
+ width: 100px;
+ left: 400px;
+ transform-style: preserve-3d;
+ }
+
+ #parent {
+ transform: translateX(0px);
+ }
+
+ #child {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ transform-style: preserve-3d;
+ }
+ </style>
+</head>
+<body>
+<div id="grandparent">
+ <div id="parent">
+ <div id="child"></div>
+ </div>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1246622 **/
+
+is(document.elementFromPoint(450,50), document.getElementById("child"), "Able to hit transformed object");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug1278021.html b/layout/base/tests/test_bug1278021.html
new file mode 100644
index 0000000000..6a30e80cce
--- /dev/null
+++ b/layout/base/tests/test_bug1278021.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test for Bug 1278021</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style type="text/css">
+ #parent {
+ transform-style: preserve-3d;
+ }
+
+ #firstchild {
+ width: 100%;
+ height: 226px;
+ background-color: blue;
+ }
+
+ #secondchild {
+ display: block;
+ position: absolute;
+ top: 0px;
+ left: 0px;
+ width: 100%;
+ height: 100%;
+ opacity: 0;
+ }
+</style>
+</head>
+<body>
+<div id="parent">
+ <div id="firstchild"></div>
+ <div id="secondchild"></div>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1278021 **/
+
+is(document.elementFromPoint(50,50), document.getElementById("secondchild"), "Able to hit transformed object");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug1448730.html b/layout/base/tests/test_bug1448730.html
new file mode 100644
index 0000000000..c9d6a24417
--- /dev/null
+++ b/layout/base/tests/test_bug1448730.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1448730
+-->
+<head>
+ <title>Mozilla Bug 1448730</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1448730">Mozilla Bug 1448730</a>
+<p id="display">
+ <div id="container" style="width: 600px; height: 500px;"></div>
+</p>
+<div id="content" style="display: none;"></div>
+<pre id="test">
+</pre>
+<script>
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({"set": [['layout.accessiblecaret.enabled', true]]}, () => {
+ let iframe = document.createElement("iframe");
+ iframe.src = "bug1448730.html";
+ iframe.style.width = "100%";
+ iframe.style.height = "100%";
+ document.getElementById('container').appendChild(iframe);
+});
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug1515822.html b/layout/base/tests/test_bug1515822.html
new file mode 100644
index 0000000000..1a23a0dbab
--- /dev/null
+++ b/layout/base/tests/test_bug1515822.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 1515822 - restore scroll position for display: contents children</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <style>
+ #shell {
+ display: contents;
+ }
+ #outer {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ }
+ #inner {
+ width: 400px;
+ height: 400px;
+ background: linear-gradient(to right bottom, white, black);
+ }
+ </style>
+</head>
+<body>
+<div id="shell">
+ <div id="outer">
+ <div id="inner">
+ </div>
+ </div>
+</div>
+<script>
+const outer = document.querySelector('#outer');
+
+outer.scrollTop = 100;
+outer.scrollLeft = 100;
+document.body.offsetTop; // flush layout
+is(outer.scrollTop, 100, 'scroll position should be moved');
+is(outer.scrollLeft, 100, 'scroll position should be moved');
+
+outer.parentElement.style.display = 'none';
+document.body.offsetTop; // flush layout
+is(outer.scrollTop, 0, 'scroll position should be zero');
+is(outer.scrollLeft, 0, 'scroll position should be zero');
+
+outer.parentElement.style.display = "";
+document.body.offsetTop; // flush layout
+is(outer.scrollTop, 100, 'scroll position should be restored');
+is(outer.scrollLeft, 100, 'scroll position should be restored');
+</script>
diff --git a/layout/base/tests/test_bug1550869_video.html b/layout/base/tests/test_bug1550869_video.html
new file mode 100644
index 0000000000..8b26533597
--- /dev/null
+++ b/layout/base/tests/test_bug1550869_video.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+ <title>Bug 1516963: Test AccessibleCaret doesn't show when clicking on an empty video container.</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ border: 1px solid blue;
+ width: 100px;
+ height: 100px;
+ background-color: yellow;
+ }
+ </style>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ document.addEventListener("selectionchange", () => {
+ ok(window.getSelection().isCollapsed, "The selection should be collapsed!");
+ });
+
+ function click() {
+ ok(window.getSelection().isCollapsed, "The selection should start collapsed!");
+ let container = document.getElementById("container");
+ synthesizeMouseAtCenter(container, {});
+ setTimeout(() => {
+ ok(window.getSelection().isCollapsed, "The selection should remain collapsed!");
+ SimpleTest.finish();
+ });
+ }
+ </script>
+ <body onload="SimpleTest.waitForFocus(click);">
+ <video id="container"></video>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug1714640.html b/layout/base/tests/test_bug1714640.html
new file mode 100644
index 0000000000..3e2de0df12
--- /dev/null
+++ b/layout/base/tests/test_bug1714640.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<meta charset="utf-8">
+<title>Bug NNN</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+<style type="text/css">
+ @font-face {
+ font-family: Ahem;
+ src: url("Ahem.ttf");
+ }
+
+ pre {
+ font: 14px/1 Ahem;
+ height: 50px;
+ overflow-y: scroll;
+ }
+</style>
+<pre id=target contenteditable>ABC
+
+
+
+
+
+<br></pre>
+<script>
+ getSelection().collapse(target.childNodes[0], 9);
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(() => {
+ const sel = SpecialPowers.wrap(getSelection());
+ sel.scrollIntoView(0, true, 100, 0);
+ is(target.scrollTop, target.scrollTopMax);
+
+ SimpleTest.finish();
+ });
+</script>
diff --git a/layout/base/tests/test_bug1756118.html b/layout/base/tests/test_bug1756118.html
new file mode 100644
index 0000000000..ed0d553c7c
--- /dev/null
+++ b/layout/base/tests/test_bug1756118.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1756118
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1756118: A removed ResizeObserver shouldn't keep the refresh driver ticking</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1756118">Mozilla Bug 1756118</a>
+<div id="display">
+ <div id="resizeMe"></div>
+</div>
+<pre id="test">
+<script>
+"use strict";
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("need to allow time to pass so that we can " +
+ "detect unwanted extra refresh driver ticks");
+const gUtils = SpecialPowers.getDOMWindowUtils(window);
+
+async function addAndRemoveResizeObserver(shouldResizeTarget) {
+ // Register a resize observer:
+ let ro = new ResizeObserver(function() { info("ResizeObserver callback"); });
+ ro.observe(resizeMe);
+
+ // double-rAF to flush pending paints:
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+
+ ok(gUtils.refreshDriverHasPendingTick,
+ "Expecting refresh driver to be ticking, with ResizeObserver registered");
+
+ // Unregister resize observer:
+ ro.unobserve(resizeMe);
+
+ // double-rAF to flush pending paints:
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+
+ // ...and importantly: make one additional change to the document.
+ // This causes a refresh driver tick, which in buggy builds causes
+ // the refresh driver to keep ticking indefinitely. (In buggy builds, this
+ // bonus tick notifies our ResizeObserverController, and it never unregisters
+ // and hence keeps the refresh driver ticking from that point on.)
+ resizeMe.style.height = "100px";
+}
+
+async function expectTicksToStop() {
+ let didStopTicking = false;
+ // Note: The maximum loop count here is an arbitrary large value, just to let
+ // us gracefully handle edge cases where multiple setTimeouts resolve before
+ // a pending refresh driver tick. Really, we just want to be sure the refresh
+ // driver *eventually* stops ticking, and we can do so gracefully by polling
+ // with some generous-but-finite number of checks here.
+ for (var i = 0; i < 100; i++) {
+ await new Promise(r => setTimeout(r, 8));
+ if(!gUtils.refreshDriverHasPendingTick) {
+ didStopTicking = true;
+ break;
+ }
+ }
+ ok(didStopTicking, "refresh driver should have eventually stopped ticking");
+}
+
+async function run() {
+ // By default, the refresh driver ticks on its own for some period of time
+ // after pageload. Turn that off so we don't have to wait it out:
+ await SpecialPowers.pushPrefEnv({'set':
+ [['layout.keep_ticking_after_load_ms', 0]]});
+
+ // Start out with a double-rAF, to flush paints from pageload:
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+
+ await addAndRemoveResizeObserver(true);
+ await expectTicksToStop();
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug332655-1.html b/layout/base/tests/test_bug332655-1.html
new file mode 100644
index 0000000000..e3d593a29a
--- /dev/null
+++ b/layout/base/tests/test_bug332655-1.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=332655
+-->
+<head>
+ <title>Test for Bug 332655</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=332655">Mozilla Bug 332655</a>
+<p id="display"></p>
+<div id="content">
+<input type="text" id="testInput"
+ style="-moz-appearance: none"> <!-- bug 1204897 workaround -->
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 332655 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function test() {
+ var textInput = $("testInput");
+ var s1, s2, s3, equal, str1, str2;
+
+ textInput.focus();
+ sendString("ab ");
+ sendString("\u05d0\u05d1");
+ s1 = snapshotWindow(window);
+
+ sendString(" ");
+ s2 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s2, true);
+ ok(equal, "space after LTR + RTL shouldn't change direction: expected " +
+ str1 + " but got " + str2);
+
+ synthesizeKey("KEY_Backspace");
+ s3 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s3, true);
+ ok(equal, "backspace should restore the status quo: expected " + str1 +
+ " but got " + str2);
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug332655-2.html b/layout/base/tests/test_bug332655-2.html
new file mode 100644
index 0000000000..aa853321ca
--- /dev/null
+++ b/layout/base/tests/test_bug332655-2.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=332655
+-->
+<head>
+ <title>Test for Bug 332655</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=332655">Mozilla Bug 332655</a>
+<p id="display"></p>
+<div id="content">
+<input type="text" id="testInput"
+ style="-moz-appearance: none"> <!-- bug 1234659 workaround -->
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 332655 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function test() {
+ var textInput = $("testInput");
+ var s1, s2, s3, equal, str1, str2;
+
+ textInput.focus();
+ sendString("\u05d0");
+ sendString("ab ");
+ sendString("\u05d1\u05d2");
+ s1 = snapshotWindow(window);
+
+ // 4 LEFT to get to the beginning of the line: HOME doesn't work on OS X
+ synthesizeKey("KEY_ArrowLeft", {repeat: 4});
+ synthesizeKey("KEY_Backspace");
+ sendString("\u05d0");
+ s2 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s2, true);
+ ok(equal, "deleting and inserting RTL char at beginning of line shouldn't change: expected " +
+ str1 + " but got " + str2);
+
+ textInput.select();
+ sendString("ab ");
+ sendString("\u05d1\u05d2");
+ // 4 LEFT to get to the beginning of the line: HOME doesn't work on OS X
+ synthesizeKey("KEY_ArrowLeft", {repeat: 4});
+ sendString("\u05d0");
+
+ s3 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s3, true);
+ ok(equal, "the order entering Bidi text shouldn't change rendering: expected " +
+ str1 + " but got " + str2);
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug369950.html b/layout/base/tests/test_bug369950.html
new file mode 100644
index 0000000000..d53f6b70a9
--- /dev/null
+++ b/layout/base/tests/test_bug369950.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=369950
+-->
+<head>
+ <title>Test for Bug 369950</title>
+ <script type="application/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=369950">Mozilla Bug 369950</a>
+<p id="display">
+ <iframe id="i" src="bug369950-subframe.xml" width="200" height="100"></iframe>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 369950 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ // Can't just run our code here, because we might not have painting
+ // unsuppressed yet. Do it async.
+ SimpleTest.executeSoon(doTheTest);
+});
+
+function doTheTest() {
+ // do a layout flush
+ var rect = $("i").getBoundingClientRect();
+ var rect2 = $("i").contentDocument.documentElement.getBoundingClientRect();
+
+ // And do the rest of it later
+ SimpleTest.executeSoon(reallyDoTheTest);
+}
+
+function reallyDoTheTest() {
+ var rect = $("i").getBoundingClientRect();
+ var rect2 = $("i").contentDocument.documentElement.getBoundingClientRect();
+
+ // We want coords relative to the iframe, so subtract off rect2.left/top.
+ // 7px is a guess to get us from the bottom of the iframe into the scrollbar
+ // groove for the horizontal scrollbar on the bottom.
+ synthesizeMouse($("i").contentDocument.documentElement,
+ -rect2.left + rect.width / 2, rect.height - rect2.top - 7,
+ {}, $("i").contentWindow);
+ // Scroll is async, so give it time
+ SimpleTest.executeSoon(checkScroll);
+};
+
+function checkScroll() {
+ // do a layout flush
+ var rect = $("i").getBoundingClientRect();
+ // And do the rest of it later
+ SimpleTest.executeSoon(reallyCheckScroll);
+}
+
+function reallyCheckScroll() {
+ var rect = $("i").getBoundingClientRect();
+ var rect2 = $("i").contentDocument.documentElement.getBoundingClientRect();
+ isnot($("i").contentWindow.scrollX, 0, "Clicking scrollbar should scroll");
+
+ // Not doing things below here, since avoiding the scroll arrows
+ // cross-platform is a huge pain.
+ SimpleTest.finish();
+ return;
+
+ // 8px horizontal offset is a guess to get us into the scr
+ synthesizeMouse($("i").contentDocument.documentElement, -rect2.left + 8,
+ rect.height - rect2.top - 7, {}, $("i").contentWindow);
+ // Scroll is async, so give it time
+ SimpleTest.executeSoon(finishUp);
+}
+
+function finishUp() {
+ is($("i").contentWindow.scrollX, 0, "Clicking scrollbar should scroll back");
+ SimpleTest.finish();
+};
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug370436.html b/layout/base/tests/test_bug370436.html
new file mode 100644
index 0000000000..e3d13ca183
--- /dev/null
+++ b/layout/base/tests/test_bug370436.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=370436
+-->
+<head>
+ <title>Test for Bug 370436</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script class="testbody" type="application/javascript">
+words = new Array()
+
+function expandStringOffsetToWord(data, offset)
+{
+ if (data == undefined) return "";
+
+ var m1 = data.substr(0, offset).match(/\w+$/) || "";
+ var m2 = data.substr(offset).match(/^\w+/) || "";
+ return m1 + m2;
+}
+
+function onContextMenu(e)
+{
+ var node = SpecialPowers.wrap(e).rangeParent;
+ var offset = e.rangeOffset;
+
+ var word = expandStringOffsetToWord(node.data, offset);
+ words.push(word);
+}
+
+function startTest()
+{
+ var ta = document.getElementById('blah');
+ ta.focus();
+ ta.selectionStart = ta.selectionEnd = ta.value.length;
+
+ // Note: This test, intentionally or by accident, relies on sending button '0'
+ // with contextMenu, which triggers some key-equiv stuff in
+ // PresShell::AdjustContextMenuKeyEvent.
+ var mouseParams = { type: 'contextmenu', button: 0 };
+
+ /* Put cursor at start and middle of "sheep" */
+ synthesizeKey("KEY_ArrowUp")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+ synthesizeKey("KEY_ArrowRight")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+ synthesizeKey("KEY_ArrowRight")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+
+ /* Put cursor at the end of "hello" */
+ synthesizeKey("KEY_ArrowUp")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+ synthesizeKey("KEY_ArrowRight")
+ synthesizeKey("KEY_ArrowRight")
+ synthesizeKey("KEY_ArrowRight")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+ synthesizeKey("KEY_ArrowRight")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+
+ /* Put cursor on "welcome" */
+ synthesizeKey("KEY_ArrowUp")
+ synthesizeMouse(ta, 0, 0, mouseParams);
+
+ is(words.pop(), "welcome", "Word 1 selected correctly");
+ is(words.pop(), "world" , "Word 2 selected correctly");
+ is(words.pop(), "hello" , "Word 3 selected correctly");
+ is(words.pop(), "hello" , "Word 4 selected correctly");
+ is(words.pop(), "sheep" , "Word 5 selected correctly");
+ is(words.pop(), "sheep" , "Word 6 selected correctly");
+ is(words.pop(), "sheep" , "Word 7 selected correctly");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish()
+SimpleTest.waitForFocus(startTest)
+</script>
+
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=370436">Mozilla Bug 370436</a></p>
+
+<textarea id="blah" rows="10" cols="80" oncontextmenu="onContextMenu(event); return false;">
+welcome
+hello world
+sheep
+</textarea>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug386575.xhtml b/layout/base/tests/test_bug386575.xhtml
new file mode 100644
index 0000000000..b606d9d57f
--- /dev/null
+++ b/layout/base/tests/test_bug386575.xhtml
@@ -0,0 +1,46 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=386575
+-->
+<head>
+ <title>Test for Bug 386575</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=386575">Mozilla Bug 386575</a>
+<p id="display">
+
+
+<!-- the image is http://searchfox.org/mozilla-central/source/layout/reftests/bugs/solidblue.png -->
+<!-- solidblue.png is a 16x16 image -->
+<table>
+ <tbody>
+ <div style="height:20px; min-height:100%">
+ <img src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%01sRGB%00%AE%CE%1C%E9%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%20cHRM%00%00z%26%00%00%80%84%00%00%FA%00%00%00%80%E8%00%00u0%00%00%EA%60%00%00%3A%98%00%00%17p%9C%BAQ%3C%00%00%00%18tEXtSoftware%00Paint.NET%20v3.01%1C%AE%24E%00%00%00'IDAT8Ocd%10%AE%FF%CF%40%11%00%19%40%09%A6H3%D8%F5%94%D8%3Ej%00%24%F6F%03q4%0C%80%E9%00%00%D6%11y%92%15%8F%DC%CE%00%00%00%00IEND%AEB%60%82" style="height: 80%" />
+ </div>
+ </tbody>
+</table>
+
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+
+/** Test for Bug 386575 **/
+
+
+SimpleTest.waitForExplicitFinish();
+ok(true,"This is an assertion test");
+SimpleTest.finish();
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug388019.html b/layout/base/tests/test_bug388019.html
new file mode 100644
index 0000000000..17cb35657c
--- /dev/null
+++ b/layout/base/tests/test_bug388019.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=388019
+-->
+<head>
+ <title>Test for Bug 388019</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=388019">Mozilla Bug 388019</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 388019 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run()
+{
+ var oldY = window.scrollY;
+ window.location.hash="#abc";
+ var newY = window.scrollY;
+ isnot(oldY, newY, "we scroll at all");
+ ok(oldY + 4000 < newY, "we scroll at least 4000 pixels");
+ window.scrollTo(0, 0); // make the results visible
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+
+<div style="height:4000px"></div>
+<a name="abc"></a>You should see this text if you click on the link.
+<div style="height:4000px"></div>
+
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug394057.html b/layout/base/tests/test_bug394057.html
new file mode 100644
index 0000000000..7cadd40753
--- /dev/null
+++ b/layout/base/tests/test_bug394057.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<html lang="en">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=394057
+-->
+<head>
+ <title>Test for Bug 394057</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+
+ #display { background: yellow; color: black; font-family: serif; }
+
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=394057">Mozilla Bug 394057</a>
+<table id="display"><tr><td>MmMmMm...iiiIIIlll---</td></tr></table>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 394057 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var tableElement = document.getElementById("display");
+
+var CC = SpecialPowers.Cc;
+var CI = SpecialPowers.Ci;
+
+var fe =
+ CC["@mozilla.org/gfx/fontenumerator;1"].createInstance(CI.nsIFontEnumerator);
+var serifFonts = fe.EnumerateFonts("x-western", "serif");
+var monospaceFonts = fe.EnumerateFonts("x-western", "monospace");
+
+function table_width_for_font(font) {
+ tableElement.style.fontFamily = '"' + font + '"';
+ var result = tableElement.offsetWidth;
+ tableElement.style.fontFamily = "";
+ return result;
+}
+
+var serifIdx = 0;
+var monospaceIdx = 0;
+var monospaceWidth, serifWidth;
+monospaceWidth = table_width_for_font(monospaceFonts[monospaceIdx]);
+for (serifIdx in serifFonts) {
+ serifWidth = table_width_for_font(serifFonts[serifIdx]);
+ if (serifWidth != monospaceWidth)
+ break;
+}
+if (serifWidth == monospaceWidth) {
+ for (monospaceIdx in monospaceFonts) {
+ monospaceWidth = table_width_for_font(monospaceFonts[monospaceIdx]);
+ if (serifWidth != monospaceWidth)
+ break;
+ }
+}
+
+isnot(serifWidth, monospaceWidth,
+ "can't find serif and monospace fonts of different width");
+
+SpecialPowers.pushPrefEnv({'set': [['font.name.serif.x-western', serifFonts[serifIdx]]]}).then(step2);
+
+var serifWidthFromPref;
+function step2() {
+ serifWidthFromPref = tableElement.offsetWidth;
+ SpecialPowers.pushPrefEnv({'set': [['font.name.serif.x-western', monospaceFonts[monospaceIdx]]]}).then(step3);
+}
+var monospaceWidthFromPref;
+function step3() {
+ monospaceWidthFromPref = tableElement.offsetWidth;
+
+ is(serifWidthFromPref, serifWidth,
+ "changing font pref should change width of table (serif)");
+ is(monospaceWidthFromPref, monospaceWidth,
+ "changing font pref should change width of table (monospace)");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug399284.html b/layout/base/tests/test_bug399284.html
new file mode 100644
index 0000000000..452ab7c77d
--- /dev/null
+++ b/layout/base/tests/test_bug399284.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=399284
+-->
+<head>
+ <title>Test for Bug 399284</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=399284">Mozilla Bug 399284</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+/** Test for Bug 399284 **/
+const testContent = "<p id='testPara'>The quick brown fox jumps over the lazy dog";
+
+var decoders = [
+ "Big5",
+ "Big5-HKSCS",
+ "EUC-JP",
+ "EUC-KR",
+ "gb18030",
+ "IBM866",
+ "ISO-2022-JP",
+ "ISO-8859-3",
+ "ISO-8859-4",
+ "ISO-8859-5",
+ "ISO-8859-6",
+ "ISO-8859-7",
+ "ISO-8859-8",
+ "ISO-8859-8-I",
+ "ISO-8859-10",
+ "ISO-8859-13",
+ "ISO-8859-14",
+ "ISO-8859-15",
+ "ISO-8859-16",
+ "ISO-8859-2",
+ "KOI8-R",
+ "KOI8-U",
+ "Shift_JIS",
+ "windows-1250",
+ "windows-1251",
+ "windows-1252",
+ "windows-1253",
+ "windows-1254",
+ "windows-1255",
+ "windows-1256",
+ "windows-1257",
+ "windows-1258",
+ "windows-874",
+ "x-mac-cyrillic",
+ "UTF-8",
+ "UTF-16LE",
+ "UTF-16BE"
+];
+
+var decoder;
+for (var i = 0; i < decoders.length; i++) {
+ var decoder = decoders[i];
+ var data;
+
+ // encode the content for non-ASCII compatible encodings
+ if (decoder == "UTF-16BE")
+ data = encodeUTF16BE(testContent);
+ else if (decoder == "UTF-16LE")
+ data = encodeUTF16LE(testContent);
+ else
+ data = encodeURI(testContent);
+ var dataURI = "data:text/html;charset=" + decoder + "," + data;
+
+ var testFrame = document.createElement("iframe");
+ frameID = decoder;
+ testFrame.setAttribute("id", frameID);
+ var testFrameObj = document.body.appendChild(testFrame);
+ testFrameObj.setAttribute("onload", "testFontSize('" + decoder + "')");
+ testFrameObj.contentDocument.location.assign(dataURI);
+}
+
+function encodeUTF16BE(string)
+{
+ var encodedString = "";
+ for (i = 0; i < string.length; ++i) {
+ encodedString += "%00";
+ encodedString += encodeURI(string.charAt(i));
+ }
+ return encodedString;
+}
+
+function encodeUTF16LE(string)
+{
+ var encodedString = "";
+ for (i = 0; i < string.length; ++i) {
+ encodedString += encodeURI(string.charAt(i));
+ encodedString += "%00";
+ }
+ return encodedString;
+}
+
+function testFontSize(frame)
+{
+ var iframeDoc = SpecialPowers.wrap($(frame)).contentDocument;
+ var size = parseInt(iframeDoc.defaultView.
+ getComputedStyle(iframeDoc.getElementById("testPara")).
+ getPropertyValue("font-size"));
+ ok(size > 0, "font size assigned for " + frame);
+}
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug399951.html b/layout/base/tests/test_bug399951.html
new file mode 100644
index 0000000000..9be4eaae4e
--- /dev/null
+++ b/layout/base/tests/test_bug399951.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=399951
+-->
+<head>
+ <title>Test for Bug 399951</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body style="direction: rtl" onload="document.body.style.direction = 'ltr';">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=399951">Mozilla Bug 399951</a>
+<div style="white-space: pre;">
+.i
+ h
+ f
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ function test()
+{
+/** Test for Bug 399951 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(test, 500);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug404209.xhtml b/layout/base/tests/test_bug404209.xhtml
new file mode 100644
index 0000000000..8ae2a01e5a
--- /dev/null
+++ b/layout/base/tests/test_bug404209.xhtml
@@ -0,0 +1,47 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=404209
+-->
+<head>
+ <title>Test for Bug 404209</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ div::first-letter { color: green; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=404209">Mozilla Bug 404209</a>
+<table id="table" dir="rtl"><div><span id="v"><span><tfoot></tfoot>abcd</span></span></div></table>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+function boom1()
+{
+ document.getElementById("table").style.borderRight = "1px solid magenta";
+ setTimeout(boom2, 400);
+}
+
+function boom2()
+{
+ var v = document.getElementById("v");
+ var newTD = document.createElementNS("http://www.w3.org/1999/xhtml", "td");
+ newTD.setAttribute("width", "13%");
+ v.insertBefore(newTD, v.firstChild);
+ setTimeout(lastTest, 400);
+}
+
+function lastTest()
+{
+/** Test for Bug 404209 **/
+ ok(true, "Should not crash");
+ SimpleTest.finish();
+}
+
+ SimpleTest.requestFlakyTimeout("untriaged");
+ setTimeout(boom1, 400);
+ SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug416896.html b/layout/base/tests/test_bug416896.html
new file mode 100644
index 0000000000..894b3939c0
--- /dev/null
+++ b/layout/base/tests/test_bug416896.html
@@ -0,0 +1,64 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=416896
+-->
+<head>
+ <title>Test for Bug 416896</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <link rel="stylesheet" type="text/css" id="l"
+ href="data:text/css,a { color: green }"/>
+ <style type="text/css" id="i"> a { color: blue; } </style>
+
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=416896">Mozilla Bug 416896</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 416896 **/
+
+ const InspectorUtils = SpecialPowers.InspectorUtils;
+
+ var inlineSheet = $("i").sheet;
+ isnot(inlineSheet, null, "Should have sheet here");
+
+ var linkedSheet = $("l").sheet;
+ isnot(linkedSheet, null, "Should have sheet here");
+
+ var inspectedRules = InspectorUtils.getCSSStyleRules(document.links[0]);
+
+ var seenInline = false;
+ var seenLinked = false;
+
+ for (var i = 0; i < inspectedRules.length; ++i)
+ {
+ var rule = inspectedRules[i];
+ var sheet = rule.parentStyleSheet;
+ if (SpecialPowers.unwrap(sheet) == inlineSheet) {
+ is(sheet.href, null, "It's an inline sheet");
+ is(seenInline, false, "Only one inline rule matches");
+ seenInline = true;
+ } else {
+ isnot(sheet.href, null, "Shouldn't have null href here " + i);
+ if (SpecialPowers.unwrap(sheet) == linkedSheet) {
+ is(seenLinked, false, "Only one linked rule matches");
+ seenLinked = true;
+ }
+ }
+ }
+
+ is(seenLinked, true, "Didn't find the linked rule?");
+ is(seenInline, true, "Didn't find the inline rule?");
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug423523.html b/layout/base/tests/test_bug423523.html
new file mode 100644
index 0000000000..69ff8e84f8
--- /dev/null
+++ b/layout/base/tests/test_bug423523.html
@@ -0,0 +1,104 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=423523
+-->
+<head>
+ <title>Test for Bug 423523</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body onload="setTimeout(runtests, 200)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=423523">Mozilla Bug 423523</a>
+<p id="display"></p>
+
+ <table>
+ <tbody><tr>
+ <td class="tdABB" id="tdTo">
+ <p id="par1">Some text...</p></td>
+ <td>
+ <div id="div1" style="border: 1px solid silver; width: 250px;" contenteditable="true">This is some editable text.</div>
+ </td></tr>
+ </tbody></table>
+
+
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 423523 **/
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+
+
+ function divIsFocused() {
+ // Check if div is directly focused.
+ var divNode = document.getElementById("div1");
+ if (window.getSelection().focusNode == divNode) {
+ return true;
+ }
+ // Check if one of the div's children has focus.
+ var node = window.getSelection().focusNode;
+ var childNodes = divNode.childNodes;
+ for (var i=0; i<childNodes.length; i++) {
+ if (childNodes[i] == node) {
+ return true;
+ }
+ }
+ // Not focused (at least not the first gen kids, and
+ // that's ok for this test).
+ return false;
+ }
+
+ function selectionOffsetIs(expectedOffset) {
+ return window.getSelection().focusOffset == expectedOffset;
+ }
+
+ function sendMouseClick() {
+ var rect=document.getElementById('div1').getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mousedown', rect.left+1, rect.top+1, 0, 1, 0);
+ utils.sendMouseEvent('mouseup', rect.left+1, rect.top+1, 0, 1, 0);
+ }
+
+ function runtests() {
+ sendMouseClick();
+ window.getSelection().collapse(document.getElementById("div1").firstChild, 0);
+ ok(divIsFocused(), "Div should be focused [0].");
+
+ ok(divIsFocused(), "Div should be focused [1].");
+ ok(selectionOffsetIs(0), "Caret should be at offset 0");
+
+ synthesizeKey("KEY_ArrowLeft");
+ ok(divIsFocused(), "Div should be focused [2].");
+ ok(selectionOffsetIs(0), "Caret should be at offset 0");
+
+ synthesizeKey("KEY_ArrowRight");
+ ok(divIsFocused(), "Div should be focused [3].");
+ ok(selectionOffsetIs(1), "Caret should be at offset 1");
+
+ synthesizeKey("KEY_ArrowLeft");
+ ok(divIsFocused(), "Div should be focused [4].");
+ ok(selectionOffsetIs(0), "Caret should be at offset 0");
+
+ ok(divIsFocused(), "Div should be focused [5].");
+ ok(selectionOffsetIs(0), "Caret should be at offset 0");
+ sendMouseClick();
+
+ ok(divIsFocused(), "Div should be focused [6].");
+ ok(selectionOffsetIs(0), "Caret should be at offset 0");
+ synthesizeKey("KEY_ArrowLeft");
+
+ ok(divIsFocused(), "Div should be focused [7].");
+ ok(selectionOffsetIs(0), "Caret should be at offset 0");
+ SimpleTest.finish();
+ }
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug435293-interaction.html b/layout/base/tests/test_bug435293-interaction.html
new file mode 100644
index 0000000000..e3002ef7f2
--- /dev/null
+++ b/layout/base/tests/test_bug435293-interaction.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=435293
+-->
+<head>
+ <title>Test for Bug 435293</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style>
+ #test1 {
+ background: green;
+ height: 100px;
+ width: 100px;
+ -moz-transform: skew(30deg, 60deg) scale(2, 5) rotate(45deg) translate(2%, 50px);
+ -moz-transform-origin: 100% 50%;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435293">Mozilla Bug 435293</a>
+<div id="content">
+ <div id="test1" onclick="testFinish();">
+ test
+ </div>
+
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+runtests();
+
+function runtests() {
+ function doClick() {
+ sendMouseEvent({type: 'click'}, 'test1');
+ }
+ setTimeout(doClick, 300);
+}
+
+function testFinish(){
+ ok(1, "We can still interact with the item after it is transformed");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug435293-scale.html b/layout/base/tests/test_bug435293-scale.html
new file mode 100644
index 0000000000..5abda33c7e
--- /dev/null
+++ b/layout/base/tests/test_bug435293-scale.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=435293
+-->
+<head>
+ <title>Test for Bug 435293</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style>
+ .test {
+ background: green;
+ height: 100px;
+ width: 100px;
+ }
+ #test1 {
+ -moz-transform: scalex(0.5);
+ }
+ #test2 {
+ -moz-transform: scaley(0.5);
+ }
+ #test3 {
+ -moz-transform: scale(0.5, 0.5);
+ }
+ #test4 {
+ -moz-transform: scale(0.5, 0.5, 0.5);
+ }
+ #test5 {
+ -moz-transform: scale(80%, none);
+ }
+ #test6 {
+ -moz-transform: scale(640000, 0.0000000000000000001);
+ }
+ #test7 {
+ -moz-transform: scale(2em, 4px);
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435293">Mozilla Bug 435293</a>
+<p id="display"></p>
+<div id="content">
+ <div id="test1" class="test">
+ test
+ </div>
+ <p id="test2" class="test">
+ test
+ </p>
+ <div id="test3" class="test">
+ test
+ </div>
+ <div id="test4" class="test">
+ test
+ </div>
+ <div id="test5" class="test">
+ test
+ </div>
+ <div id="test6" class="test">
+ test
+ </div>
+ <div id="test7" class="test">
+ test
+ </div>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+runtests();
+
+function runtests() {
+ var style = window.getComputedStyle(document.getElementById("test1"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(0.5, 0, 0, 1, 0, 0)",
+ "Scalex proper matrix is applied");
+
+ style = window.getComputedStyle(document.getElementById("test2"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(1, 0, 0, 0.5, 0, 0)",
+ "Scaley proper matrix is applied");
+
+ style = window.getComputedStyle(document.getElementById("test3"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(0.5, 0, 0, 0.5, 0, 0)",
+ "Scale proper matrix is applied");
+
+ style = window.getComputedStyle(document.getElementById("test4"));
+ is(style.getPropertyValue("-moz-transform"), "none",
+ "Three dimensional scale should be ignored");
+
+ style = window.getComputedStyle(document.getElementById("test5"));
+ is(style.getPropertyValue("-moz-transform"), "none",
+ "Percent values in scale should be ignored");
+
+ style = window.getComputedStyle(document.getElementById("test6"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(640000, 0, 0, 1e-19, 0, 0)",
+ "Ensure wacky values are accepted");
+
+ style = window.getComputedStyle(document.getElementById("test7"));
+ is(style.getPropertyValue("-moz-transform"), "none",
+ "No unit values allowed in scale");
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug435293-skew.html b/layout/base/tests/test_bug435293-skew.html
new file mode 100644
index 0000000000..acabf90760
--- /dev/null
+++ b/layout/base/tests/test_bug435293-skew.html
@@ -0,0 +1,173 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=435293
+-->
+<head>
+ <title>Test for Bug 435293</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style>
+ /* Skewed boxes can get very big. The .pane wrapper prevents them
+ from obscuring the test results. */
+ .pane {
+ height: 300px;
+ width: 300px;
+ float: left;
+ overflow: auto;
+ border: 1px solid black;
+ }
+ .test {
+ background: green;
+ height: 100px;
+ width: 100px;
+ margin: 100px;
+ }
+
+ /* Radian units are not used in this test because our CSS
+ implementation stores all dimensional values in single-
+ precision floating point, which makes it impossible to
+ hit mathematically interesting angles with radians.
+ Degrees and grads do not suffer this problem. */
+ #test1 {
+ -moz-transform: skewx(30deg);
+ }
+ #test2 {
+ -moz-transform: skewy(60deg);
+ }
+ #test3 {
+ -moz-transform: skew(45deg, 45deg);
+ }
+ #test4 {
+ -moz-transform: skew(360deg, 45deg);
+ }
+ #test5 {
+ -moz-transform: skew(45deg, 150grad);
+ }
+ #test6 {
+ -moz-transform: skew(80%, 78px);
+ }
+ #test7 {
+ -moz-transform: skew(2em, 40ex);
+ }
+ #test8 {
+ -moz-transform: skew(-45deg, -465deg);
+ }
+ #test9 {
+ -moz-transform: skew(30deg, 30deg, 30deg);
+ }
+
+ /* approach the singularity from the negative side */
+ #test10 {
+ -moz-transform: skew(50grad, 90.001deg);
+ }
+ #test11 {
+ -moz-transform: skew(300grad, 90.001deg);
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=435293">Mozilla Bug 435293</a>
+<p id="display"></p>
+<div id="content">
+ <div class="pane"><div id="test1" class="test">test</div></div>
+ <div class="pane"><p id="test2" class="test">test</p></div>
+ <div class="pane"><div id="test3" class="test">test</div></div>
+ <div class="pane"><div id="test4" class="test">test</div></div>
+ <div class="pane"><div id="test5" class="test">test</div></div>
+ <div class="pane"><div id="test6" class="test">test</div></div>
+ <div class="pane"><div id="test7" class="test">test</div></div>
+ <div class="pane"><div id="test8" class="test">test</div></div>
+ <div class="pane"><div id="test9" class="test">test</div></div>
+ <div class="pane"><div id="test10" class="test">test</div></div>
+ <div class="pane"><div id="test11" class="test">test</div></div>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+runtests();
+
+function runtests() {
+ // For test 1 we need to handle the contingency that different systems may
+ // round differently. We will parse out the values and compare them
+ // individually. The matrix should be: matrix(1, 0, 0.57735, 1, 0, 0)
+ var style = window.getComputedStyle(document.getElementById("test1"));
+ var tformStyle = style.getPropertyValue("-moz-transform");
+ var tformValues = tformStyle.substring(tformStyle.indexOf('(') + 1,
+ tformStyle.indexOf(')')).split(',');
+ is((+tformValues[0]), 1, "Test1: skewx: param 0 is 1");
+ is((+tformValues[1]), 0, "Test1: skewx: param 1 is 0");
+ ok(verifyRounded(tformValues[2], 0.57735), "Test1: skewx: Rounded param 2 is in bounds");
+ is((+tformValues[3]), 1, "Test1: skewx: param 3 is 1");
+ is((+tformValues[4]), 0, "Test1: skewx: param 4 is 0");
+ is((+tformValues[5]), 0, "Test1: skewx: param 5 is 0");
+
+ // Again, handle rounding for test 2, proper matrix should be:
+ // matrix(1, 1.73205, 0, 1, 0, 0)
+ style = window.getComputedStyle(document.getElementById("test2"));
+ tformStyle = style.getPropertyValue("-moz-transform");
+ tformValues = tformStyle.substring(tformStyle.indexOf('(') + 1,
+ tformStyle.indexOf(')')).split(',');
+ is((+tformValues[0]), 1, "Test2: skewy: param 0 is 1");
+ ok(verifyRounded(tformValues[1], 1.73205), "Test2: skewy: Rounded param 1 is in bounds");
+ is((+tformValues[2]), 0, "Test2: skewy: param 2 is 0");
+ is((+tformValues[3]), 1, "Test2: skewy: param 3 is 1");
+ is((+tformValues[4]), 0, "Test2: skewy: param 4 is 0");
+ is((+tformValues[5]), 0, "Test2: skewy: param 5 is 0");
+
+ style = window.getComputedStyle(document.getElementById("test3"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(1, 1, 1, 1, 0, 0)",
+ "Test3: Skew proper matrix is applied");
+
+ style = window.getComputedStyle(document.getElementById("test4"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(1, 1, 0, 1, 0, 0)",
+ "Test4: Skew angle wrap: proper matrix is applied");
+
+ style = window.getComputedStyle(document.getElementById("test5"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(1, -1, 1, 1, 0, 0)",
+ "Test5: Skew mixing deg and grad");
+
+ style = window.getComputedStyle(document.getElementById("test6"));
+ is(style.getPropertyValue("-moz-transform"), "none",
+ "Test6: Skew with invalid units");
+
+ style = window.getComputedStyle(document.getElementById("test7"));
+ is(style.getPropertyValue("-moz-transform"), "none",
+ "Test7: Skew with more invalid units");
+
+ // Test 8: skew with negative degrees, here again we must handle rounding.
+ // The matrix should be: matrix(1, 3.73206, -1, 1, 0, 0)
+ style = window.getComputedStyle(document.getElementById("test8"));
+ tformStyle = style.getPropertyValue("-moz-transform");
+ tformValues = tformStyle.substring(tformStyle.indexOf('(') + 1,
+ tformStyle.indexOf(')')).split(',');
+ is((+tformValues[0]), 1, "Test8: Test skew with negative degrees-param 0 is 1");
+ ok(verifyRounded(tformValues[1], 3.73206), "Test8: Rounded param 1 is in bounds");
+ is((+tformValues[2]), -1, "Test8: param 2 is -1");
+ is((+tformValues[3]), 1, "Test8: param 3 is 1");
+ is((+tformValues[4]), 0, "Test8: param 4 is 0");
+ is((+tformValues[5]), 0, "Test8: param 5 is 0");
+
+ style = window.getComputedStyle(document.getElementById("test9"));
+ is(style.getPropertyValue("-moz-transform"), "none",
+ "Test9: Skew in 3d should be ignored");
+
+ style = window.getComputedStyle(document.getElementById("test10"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(1, -10000, 1, 1, 0, 0)",
+ "Test10: Skew with nearly infinite numbers");
+
+ style = window.getComputedStyle(document.getElementById("test11"));
+ is(style.getPropertyValue("-moz-transform"), "matrix(1, -10000, 10000, 1, 0, 0)",
+ "Test11: Skew with more infinite numbers");
+}
+
+// Verifies that aVal is +/- 0.00001 of aTrueVal
+// Returns true if so, false if not
+function verifyRounded(aVal, aTrueVal) {
+ return (Math.abs(aVal - aTrueVal).toFixed(5) <= 0.00001);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug449781.html b/layout/base/tests/test_bug449781.html
new file mode 100644
index 0000000000..3326bb0751
--- /dev/null
+++ b/layout/base/tests/test_bug449781.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=449781
+-->
+<head>
+ <title>Test for Bug 449781</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.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=449781">Mozilla Bug 449781</a>
+<p id="display">Canary</p>
+<iframe src="about:blank" id="ourFrame" style="visibility: hidden">
+</iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var s1, s2, s3, s4;
+
+/** Test for Bug 449781 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ s1 = snapshotWindow(window);
+
+ $("ourFrame").style.display = "none";
+ is($("ourFrame").offsetWidth, 0, "Unexpected width after hiding");
+ $("ourFrame").style.display = "";
+ is($("ourFrame").clientWidth, 300, "Unexpected width after showing");
+
+ s2 = snapshotWindow(window);
+
+ var equal, str1, str2;
+ [equal, str1, str2] = compareSnapshots(s1, s2, true);
+ ok(equal, "Show/hide should have no effect - " +
+ "got " + str1 + " but expected " + str2);
+
+ SpecialPowers.setFullZoom($("ourFrame").contentWindow, 2);
+
+ s3 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s3, true);
+ ok(equal, "Zoom should have no effect - " +
+ "got " + str1 + " but expected " + str2);
+
+ $("display").style.display = "none";
+
+ s4 = snapshotWindow(window);
+ [equal, str1, str2] = compareSnapshots(s3, s4, true);
+ ok(!equal, "Should be able to see the canary");
+
+ SimpleTest.finish();
+});
+
+
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug450930.xhtml b/layout/base/tests/test_bug450930.xhtml
new file mode 100644
index 0000000000..b899724462
--- /dev/null
+++ b/layout/base/tests/test_bug450930.xhtml
@@ -0,0 +1,28 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml" xmlns:svg="http://www.w3.org/2000/svg">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=450930
+-->
+<head>
+ <title>Test for Bug 450930 (MozAfterPaint)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript"><![CDATA[
+
+/** Test for Bug 450930 **/
+SimpleTest.waitForExplicitFinish();
+var subwindow = window.open("./bug450930.xhtml", "bug450930", "width=800,height=1000");
+
+function finishTests() {
+ subwindow.close();
+ SimpleTest.finish();
+}
+
+]]></script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug469170.html b/layout/base/tests/test_bug469170.html
new file mode 100644
index 0000000000..42ab8a69f6
--- /dev/null
+++ b/layout/base/tests/test_bug469170.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=469170
+-->
+<head>
+ <title>Test for Bug 469170</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest();">
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=469170">Mozilla Bug 469170</a></p>
+
+<iframe id="source" width="50" height="50" srcdoc="<html></html>"></iframe>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 469170 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var source = document.getElementById('source').contentWindow;
+ rect = { left: 0, top: 0,
+ width: source.innerWidth, height: source.innerHeight };
+ var canvas = SpecialPowers.snapshotRect(source, rect, "transparent");
+ var context = canvas.getContext("2d");
+
+ var components = [ "red", "green", "blue", "alpha" ];
+
+ var data = context.getImageData(0, 0, canvas.width, canvas.height).data;
+ var failed = false;
+ for (var i = 0; i < data.length; i++) {
+ if (data[i] != 0) {
+ is(data[i], 0, "pixel " + Math.floor(i/4) + " " + components[i%4]);
+ failed = true;
+ }
+ }
+ if (!failed) {
+ ok(!failed, "all pixels fully transparent");
+ }
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug471126.html b/layout/base/tests/test_bug471126.html
new file mode 100644
index 0000000000..623c5bbbe1
--- /dev/null
+++ b/layout/base/tests/test_bug471126.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=471126
+-->
+<head>
+ <title>Test for Bug 471126</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=471126">Mozilla Bug 471126</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 471126 **/
+
+function test()
+{
+ var selection = window.getSelection();
+ selection.collapse(document.documentElement, 0);
+ document.documentElement.addEventListener("click", function(){ var foo = window; });
+}
+test();
+ok(true, "Shouldn't leak");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug499538-1.html b/layout/base/tests/test_bug499538-1.html
new file mode 100644
index 0000000000..0f5d7f3d5f
--- /dev/null
+++ b/layout/base/tests/test_bug499538-1.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=499538
+-->
+<head>
+ <title>Test for Bug 499538</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=499538">Mozilla Bug 499538</a>
+<p id="display"></p>
+<div id="content">
+<input type="text" id="testInput" style="-moz-appearance:none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 499538 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function test() {
+ var textInput = $("testInput");
+ var s1, s2, s3, equal, str1, str2;
+
+ textInput.focus();
+ sendString("a ");
+ sendString("\u0639\u063A");
+ sendString(" b");
+ s1 = snapshotWindow(window);
+
+ textInput.select();
+ sendString("a b");
+ synthesizeKey("KEY_ArrowLeft");
+ synthesizeKey("KEY_ArrowLeft");
+ sendString("\u0639\u063A");
+ s2 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s2, true);
+ ok(equal, "Arabic text between English words not connected: expected " +
+ str1 + " but got " + str2);
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug514127.html b/layout/base/tests/test_bug514127.html
new file mode 100644
index 0000000000..35300c9eed
--- /dev/null
+++ b/layout/base/tests/test_bug514127.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=514127
+-->
+<head>
+ <title>Test for Bug 514127</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="runTest();">
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=514127">Mozilla Bug 514127</a></p>
+
+<!--
+iframe source is
+<html><body style='background: rgb(0,0,255); width: 100px; height: 50100px;'></body></html>
+-->
+<iframe id="source" width="50" height="50"
+ src="data:text/html,%3Chtml%3E%3Cbody%20style%3D%27background%3A%20rgb%280%2C0%2C255%29%3B%20width%3A%20100px%3B%20height%3A%2050100px%3B%27%3E%3C%2Fbody%3E%3C%2Fhtml%3E"></iframe>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 514127 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+
+ var source = document.getElementById('source').contentWindow;
+ var canvasWidth = 50;
+ var canvasHeight = 50;
+
+ rect = { left: 25, top: 50000,
+ width: canvasWidth, height: canvasHeight };
+ var canvas = SpecialPowers.snapshotRect(source, rect, "transparent");
+ var context = canvas.getContext("2d");
+
+ var data = context.getImageData(0, 0, canvasWidth, canvasHeight).data;
+ var failed = false;
+ for (var i = 0; i < data.length; i+=4) {
+ if (data[i] != 0 || data[i+1] != 0 || data[i+2] != 255 || data[i+3] != 255) {
+ failed = true;
+ break;
+ }
+ }
+ ok(!failed, "all pixels blue");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug518777.html b/layout/base/tests/test_bug518777.html
new file mode 100644
index 0000000000..25a2e58e73
--- /dev/null
+++ b/layout/base/tests/test_bug518777.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=518777
+-->
+<head>
+ <title>Test for Bug 518777</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function dotest() {
+ var canvasWidth = 50;
+ var canvasHeight = 50;
+ var source = document.getElementById("source").contentWindow;
+ rect = { left: 25, top: 25,
+ width: canvasWidth, height: canvasHeight };
+ var canvas = SpecialPowers.snapshotRect(source, rect, "transparent");
+ var context = canvas.getContext("2d");
+
+ var data = context.getImageData(0, 0, canvasWidth, canvasHeight).data;
+ var i;
+ for (i = 0; i < data.length; i += 4) {
+ if (data[i] != 0 || data[i + 1] != 0 || data[i + 2] != 255 || data[i + 3] != 255)
+ break;
+ }
+ ok(i >= data.length, "all pixels blue");
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body>
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=518777">Mozilla Bug 518777</a></p>
+
+<iframe id="source" width="50" height="50"
+ srcdoc="<html><body onload='window.scrollTo(0,99999999); document.documentElement.offsetWidth; window.parent.dotest();' style='background: rgb(0,0,255); width: 100px; height: 50100px;'></body></html>"></iframe>
+
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug548545.xhtml b/layout/base/tests/test_bug548545.xhtml
new file mode 100644
index 0000000000..d532acba34
--- /dev/null
+++ b/layout/base/tests/test_bug548545.xhtml
@@ -0,0 +1,47 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=548545
+-->
+<head>
+ <title>Test for Bug 548545</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style type="text/css">
+ #content { margin: 1em; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=548545">Mozilla Bug 548545</a>
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+<![CDATA[
+/** Test for Bug 548545 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var content = document.getElementById("content");
+
+var CC = SpecialPowers.Cc;
+var CI = SpecialPowers.Ci;
+
+var fe =
+ CC["@mozilla.org/gfx/fontenumerator;1"].createInstance(CI.nsIFontEnumerator);
+var allFonts = fe.EnumerateFonts(null, null);
+
+var idx = 0;
+var list = "";
+for (idx in allFonts) {
+ list += allFonts[idx] + "<br/>";
+}
+content.innerHTML = list;
+
+ok(true,"Loaded the font list");
+SimpleTest.finish();
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug558663.html b/layout/base/tests/test_bug558663.html
new file mode 100644
index 0000000000..ea957cc960
--- /dev/null
+++ b/layout/base/tests/test_bug558663.html
@@ -0,0 +1,37 @@
+<!-- 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/. -->
+
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Bug 558663 test</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ iframe {
+ width: 600px;
+ height: 400px;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="container"></div>
+ </body>
+ <script>
+ if (navigator.platform.startsWith("Linux")) {
+ // For e10s issue of bug 966157
+ SimpleTest.expectAssertions(0, 2);
+ }
+ SimpleTest.waitForExplicitFinish();
+ // AccessibleCaret's pref is checked only when PresShell is initialized. To turn
+ // off the pref, we test bug 558663 in an iframe.
+ SpecialPowers.pushPrefEnv({"set": [['layout.accessiblecaret.enabled_on_touch', false]]}, function() {
+ var iframe = document.createElement("iframe");
+ iframe.src = "bug558663.html";
+ document.getElementById('container').appendChild(iframe);
+ });
+ </script>
+</html>
diff --git a/layout/base/tests/test_bug559499.html b/layout/base/tests/test_bug559499.html
new file mode 100644
index 0000000000..6b70d86a70
--- /dev/null
+++ b/layout/base/tests/test_bug559499.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html style="background:yellow">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=559499
+-->
+<head>
+ <title>Test for Bug 559499</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body style="position:relative; z-index:-1; padding-top:100px;">
+<p><a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=559499">Mozilla Bug 514127</a></p>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 559499 **/
+
+is(document.elementFromPoint(50, 50), document.body, "Able to hit body");
+document.documentElement.style.display = "table";
+is(document.elementFromPoint(50, 50), document.body, "Able to hit body (table)");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug569520.html b/layout/base/tests/test_bug569520.html
new file mode 100644
index 0000000000..50f7d2afc1
--- /dev/null
+++ b/layout/base/tests/test_bug569520.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=569520
+-->
+<head>
+ <title>Test for Bug 569520</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=569520">Mozilla Bug 569520</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 569520 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var start = window.performance.now();
+var firstListenerArg;
+var secondListenerArg;
+var thirdListenerTime;
+
+// callback arg is in the same timeline as performance.now()
+function thirdListener(t) {
+ thirdListenerTime = t;
+
+ ok(secondListenerArg >= firstListenerArg, // callback args from consecutive requestAnimationFrame
+ "Second listener should fire after first listener");
+
+ ok(thirdListenerTime >= secondListenerArg,
+ "Third listener should fire after second listener");
+
+ ok(firstListenerArg >= start, "First listener should fire after start");
+
+ SimpleTest.finish();
+}
+
+// callback arg is from requestAnimationFrame and comparable to performance.now()
+function secondListener(t) {
+ secondListenerArg = t;
+ requestAnimationFrame(thirdListener);
+}
+
+function firstListener(t) {
+ firstListenerArg = t;
+ requestAnimationFrame(secondListener);
+}
+
+addLoadEvent(function() {
+ setTimeout(function() {
+ requestAnimationFrame(firstListener);
+ }, 100);
+ });
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug582181-1.html b/layout/base/tests/test_bug582181-1.html
new file mode 100644
index 0000000000..b8d4ee9de6
--- /dev/null
+++ b/layout/base/tests/test_bug582181-1.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=582181
+-->
+<head>
+ <title>Test for Bug 582181</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=582181">Mozilla Bug 582181</a>
+<p id="display"></p>
+<div id="content" dir="rtl">
+<textarea rows="4" style="resize: none" id="testInput">فارسی
+[[en:Farsi]]</textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 582181 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function test() {
+ var textInput = $("testInput");
+ var s1, s2, s3, equal, str1, str2;
+
+ s1 = snapshotWindow(window);
+
+ textInput.focus();
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_Enter");
+ textInput.blur();
+ s2 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s2, true);
+ ok(equal, "enter after text shouldn't change rendering: expected " +
+ str1 + " but got " + str2);
+
+ textInput.focus();
+ synthesizeKey("KEY_Backspace");
+ textInput.blur();
+ s3 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s3, true);
+ ok(equal, "backspace shouldn't change rendering: expected " + str1 +
+ " but got " + str2);
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug582181-2.html b/layout/base/tests/test_bug582181-2.html
new file mode 100644
index 0000000000..8a4d1c5116
--- /dev/null
+++ b/layout/base/tests/test_bug582181-2.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=582181
+-->
+<head>
+ <title>Test for Bug 582181</title>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=582181">Mozilla Bug 582181</a>
+<p id="display"></p>
+<div id="content" dir="rtl">
+<textarea rows="5" id="testInput" style="resize:none">Blah blah
+فلان فلان
+&lt;ref&gt;ooo&lt;/ref&gt;
+&lt;references /&gt;</textarea>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 582181 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function test() {
+ var textInput = $("testInput");
+ var s1, s2, s3, equal, str1, str2;
+
+ s1 = snapshotWindow(window);
+
+ textInput.focus();
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_Backspace");
+ textInput.blur();
+ s2 = snapshotWindow(window);
+
+ [unequal, str1, str2] = compareSnapshots(s1, s2, false);
+ ok(unequal, "backspace after text should change rendering: got " + str2);
+
+ textInput.focus();
+ sendString(">");
+ textInput.blur();
+ s3 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s3, true);
+ ok(equal, "entering '>' should restore original rendering: expected " + str1 +
+ " but got " + str2);
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug582771.html b/layout/base/tests/test_bug582771.html
new file mode 100644
index 0000000000..9c230e15ba
--- /dev/null
+++ b/layout/base/tests/test_bug582771.html
@@ -0,0 +1,128 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=582771
+-->
+<head>
+ <title>Test for Bug 582771</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .test {
+ width: 20px;
+ height: 20px;
+ border: 1px solid black;
+ -moz-user-select: none;
+ }
+ </style>
+</head>
+<body onload="setTimeout('runTest()', 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=582771">Mozilla Bug 582771</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 582771 **/
+
+SimpleTest.waitForExplicitFinish();
+var d1;
+var d2;
+var d1mousemovecount = 0;
+var d2mousemovecount = 0;
+
+function sendMouseMove(el) {
+ var rect = el.getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mousemove', rect.left + 5, rect.top + 5, 0, 0, 0);
+}
+
+function sendMouseDown(el) {
+ var rect = el.getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mousedown', rect.left + 5, rect.top + 5, 0, 1, 0);
+}
+
+function sendMouseUp(el) {
+ var rect = el.getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mouseup', rect.left + 5, rect.top + 5, 0, 1, 0);
+}
+
+function log(s) {
+ document.getElementById("l").textContent += s + "\n";
+}
+
+function d2Listener(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, d2, "d2 should have got mousemove.");
+ ++d2mousemovecount;
+}
+
+function d1Listener(e) {
+ log(e.type + ", " + e.target.id);
+ d1.setCapture(true);
+}
+
+function d1Listener2(e) {
+ log(e.type + ", " + e.target.id);
+ d2.setCapture(true);
+}
+
+function d1MouseMoveListener(e) {
+ log(e.type + ", " + e.target.id);
+ ++d1mousemovecount;
+}
+
+function runTest() {
+ d1 = document.getElementById("d1");
+ d2 = document.getElementById("d2");
+ d2.addEventListener("mousemove", d2Listener, true);
+ document.body.offsetLeft;
+ sendMouseMove(d2);
+ is(d2mousemovecount, 1, "Should have got mousemove");
+
+ // This shouldn't enable capturing, since we're not in a right kind of
+ // event listener.
+ d1.setCapture(true);
+ sendMouseDown(d1);
+ sendMouseMove(d2);
+ sendMouseUp(d1);
+ is(d2mousemovecount, 2, "Should have got mousemove");
+
+ d1.addEventListener("mousedown", d1Listener, true);
+ d1.addEventListener("mousemove", d1MouseMoveListener, true);
+ sendMouseDown(d1);
+ sendMouseMove(d2);
+ is(d2mousemovecount, 2, "Shouldn't have got mousemove");
+ is(d1mousemovecount, 1, "Should have got mousemove");
+ sendMouseUp(d1);
+ d1.removeEventListener("mousedown", d1Listener, true);
+ d1.removeEventListener("mousemove", d1MouseMoveListener, true);
+
+ // Nothing should be capturing the event.
+ sendMouseMove(d2);
+ is(d2mousemovecount, 3, "Should have got mousemove");
+
+
+ d1.addEventListener("mousemove", d1Listener2, true);
+ sendMouseDown(d1);
+ sendMouseMove(d1); // This should call setCapture to d2!
+ d1.removeEventListener("mousemove", d1Listener2, true);
+ d1.addEventListener("mousemove", d1MouseMoveListener, true);
+ sendMouseMove(d1); // This should send mouse event to d2.
+ is(d1mousemovecount, 1, "Shouldn't have got mousemove");
+ is(d2mousemovecount, 4, "Should have got mousemove");
+ sendMouseUp(d1);
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div class="test" id="d1">&nbsp;</div><br><div class="test" id="d2">&nbsp;</div>
+<pre id="l"></pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug583889.html b/layout/base/tests/test_bug583889.html
new file mode 100644
index 0000000000..84ddae90d5
--- /dev/null
+++ b/layout/base/tests/test_bug583889.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=583889
+-->
+<head>
+ <title>Test for Bug 583889</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=583889">Mozilla Bug 583889</a>
+<iframe id="inner" style="width: 10px; height: 10px;"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 583889 **/
+SimpleTest.waitForExplicitFinish();
+
+function grabEventAndGo(event) {
+ gen.next(event);
+}
+
+function* runTest() {
+ window.onload = grabEventAndGo;
+ // Wait for onLoad event.
+ yield;
+
+ var inner = $("inner");
+ inner.src = "bug583889_inner1.html";
+ window.onmessage = grabEventAndGo;
+ // Wait for message from 'inner' iframe.
+ event = yield;
+
+ while (event.data != "done") {
+ data = JSON.parse(event.data);
+ is(data.top, 300, "should remain at same top");
+ is(data.left, 300, "should remain at same left");
+
+ // Wait for message from 'inner' iframe.
+ event = yield;
+ }
+
+ // finish(), yet let the test actually end first, to be safe.
+ SimpleTest.executeSoon(SimpleTest.finish);
+}
+
+var gen = runTest();
+gen.next();
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug588174.html b/layout/base/tests/test_bug588174.html
new file mode 100644
index 0000000000..50f7d2afc1
--- /dev/null
+++ b/layout/base/tests/test_bug588174.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=569520
+-->
+<head>
+ <title>Test for Bug 569520</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=569520">Mozilla Bug 569520</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 569520 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var start = window.performance.now();
+var firstListenerArg;
+var secondListenerArg;
+var thirdListenerTime;
+
+// callback arg is in the same timeline as performance.now()
+function thirdListener(t) {
+ thirdListenerTime = t;
+
+ ok(secondListenerArg >= firstListenerArg, // callback args from consecutive requestAnimationFrame
+ "Second listener should fire after first listener");
+
+ ok(thirdListenerTime >= secondListenerArg,
+ "Third listener should fire after second listener");
+
+ ok(firstListenerArg >= start, "First listener should fire after start");
+
+ SimpleTest.finish();
+}
+
+// callback arg is from requestAnimationFrame and comparable to performance.now()
+function secondListener(t) {
+ secondListenerArg = t;
+ requestAnimationFrame(thirdListener);
+}
+
+function firstListener(t) {
+ firstListenerArg = t;
+ requestAnimationFrame(secondListener);
+}
+
+addLoadEvent(function() {
+ setTimeout(function() {
+ requestAnimationFrame(firstListener);
+ }, 100);
+ });
+
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug603550.html b/layout/base/tests/test_bug603550.html
new file mode 100644
index 0000000000..a62ebc8723
--- /dev/null
+++ b/layout/base/tests/test_bug603550.html
@@ -0,0 +1,118 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=603550
+-->
+<head>
+ <title>Test for Bug 603550</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .test {
+ width: 20px;
+ height: 20px;
+ border: 1px solid black;
+ -moz-user-select: none;
+ }
+ </style>
+</head>
+<body onload="setTimeout('runTest()', 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=603550">Mozilla Bug 603550</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 603550 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function sendMouseMoveFaraway(el) {
+ var rect = el.getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mousemove', rect.left + 5000, rect.top + 5000, 0, 0, 0);
+}
+
+function sendMouseDown(el) {
+ var rect = el.getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mousedown', rect.left + 5, rect.top + 5, 0, 1, 0);
+}
+
+function sendMouseUp(el) {
+ var rect = el.getBoundingClientRect();
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent('mouseup', rect.left + 5, rect.top + 5, 0, 1, 0);
+}
+
+function fireEvent(target, event) {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.dispatchDOMEventViaPresShellForTesting(target, event);
+}
+
+function fireDrop(element) {
+ var ds = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
+ getService(SpecialPowers.Ci.nsIDragService);
+
+ ds.startDragSessionForTests(
+ SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
+ SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_COPY |
+ SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_LINK
+ ); // Session for getting dataTransfer object.
+ try {
+ var event = document.createEvent("DragEvent");
+ event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
+ fireEvent(element, event);
+
+ event = document.createEvent("DragEvent");
+ event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, null);
+ fireEvent(element, event);
+ } finally {
+ ds.endDragSession(false);
+ ok(!ds.getCurrentSession(), "There shouldn't be a drag session anymore!");
+ }
+}
+
+function runTest() {
+ var d1 = document.getElementById("d1");
+ var didGetMouseMove = false;
+ sendMouseDown(d1);
+ document.addEventListener("mousemove",
+ function (e) {
+ didGetMouseMove = (e.target == document);
+ },
+ true);
+ sendMouseMoveFaraway(d1);
+ ok(didGetMouseMove, "Should have got mousemove!");
+ sendMouseUp(d1);
+
+ didGetMouseMove = false;
+ document.addEventListener("mousedown",
+ function (e) {
+ e.preventDefault();
+ },
+ true);
+ sendMouseDown(d1);
+ sendMouseMoveFaraway(d1);
+ ok(didGetMouseMove, "Should have got mousemove! (2)");
+ sendMouseUp(d1);
+
+ didGetMouseMove = false;
+ sendMouseDown(d1);
+ fireDrop(d1);
+ sendMouseMoveFaraway(d1);
+ ok(!didGetMouseMove, "Shouldn't have got mousemove!");
+
+
+
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+<div class="test" id="d1">&nbsp;</div>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug607529.html b/layout/base/tests/test_bug607529.html
new file mode 100644
index 0000000000..a74aff565c
--- /dev/null
+++ b/layout/base/tests/test_bug607529.html
@@ -0,0 +1,119 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=607529
+-->
+<head>
+ <title>Test for Bug 607529</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=607529">Mozilla Bug 607529</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ /* General idea: Open a new window (needed because we don't bfcache
+ subframes) that uses requestAnimationFrame, navigate it, navigate it
+ back, and verify that the animations are still running. */
+
+ function executeTest() {
+ /** Test for Bug 607529 **/
+ var doneOneLoad = false;
+ var done = false;
+ var bc = new BroadcastChannel("bug607529");
+ var bc_1 = new BroadcastChannel("bug607529_1");
+ bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ isnot(msg, "notcached", "Should never end up not being cached");
+ if (msg == "loaded") {
+ if (!doneOneLoad) {
+ doneOneLoad = true;
+ bc.postMessage("navigateToPage");
+ } else {
+ // This is unexpected, but it can happen on Android, probably when
+ // bfcache gets purged due to memory pressure. Hence, "soft fail" there.
+ var message = "onload handler shouldn't fire on restore from bfcache";
+ if (navigator.appVersion.includes("Android")) {
+ todo(false, message);
+ } else {
+ ok(false, message);
+ }
+ // In any case, more messages aren't coming, so finish up.
+ closeWindowAndFinish();
+ }
+ }
+ else if (msg == "revived") {
+ bc.postMessage("report");
+ }
+ else if (msg == "callbackHappened") {
+ // We might get this message more than once, if the other page queues up
+ // more than one callbackHappened message before we manage to close it.
+ // Protect against calling SimpleTest.finish() more than once.
+ if (!done) {
+ closeWindowAndFinish();
+ done = true;
+ }
+ } else if (msg == "closed") {
+ bc.close();
+ bc_1.close();
+ SimpleTest.finish();
+ } else {
+ try {
+ var jsonMsg = JSON.parse(msg);
+ } catch (ex) {
+ // In case JSON.parse throws, we pause to print the string that it
+ // choked on, and then resume throwing the exception.
+ ok(false, "JSON.parse threw, when passed string '" + jsonMsg + "'");
+ throw ex;
+ }
+ if (jsonMsg.error) {
+ window.onerror(jsonMsg.msg, jsonMsg.url, jsonMsg.line);
+ }
+ }
+ }
+ bc_1.onmessage = (msgEvent) => {
+ if (msgEvent.data == "goback") {
+ bc_1.postMessage("navigateBack");
+ }
+ }
+ function closeWindowAndFinish() {
+ bc.postMessage("close");
+ }
+
+ // If Fission is disabled, the pref is no-op.
+ SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open("file_bug607529.html", "", "noopener");
+ });
+ }
+
+ if (isXOrigin) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]],
+ }).then(() => {
+ SpecialPowers.addPermission("storageAccessAPI", true, window.location.href).then(() => {
+ SpecialPowers.wrap(document).requestStorageAccess().then(() => {
+ executeTest();
+ });
+ });
+ });
+ } else {
+ executeTest();
+ }
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug629838.html b/layout/base/tests/test_bug629838.html
new file mode 100644
index 0000000000..94c60a9557
--- /dev/null
+++ b/layout/base/tests/test_bug629838.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Tests for MozAfterPaint</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display">
+<div width="100" height="100" id="p" style="background-color: rgb(0,0,0)"/>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var initialPaintCount, afterPaintCount;
+var color = 0;
+
+function onAfterPaint () {
+ afterPaintCount += 1;
+}
+
+function startTest() {
+ setTimeout(function () {
+ afterPaintCount = 0;
+ initialPaintCount = SpecialPowers.DOMWindowUtils.paintCount;
+ window.addEventListener("MozAfterPaint", onAfterPaint, true);
+ doBackgroundFlicker();
+ }, 500);
+}
+
+document.addEventListener("DOMContentLoaded", startTest, true);
+
+// Unfortunately we cannot reliably assert that mozPaintCount and afterPaintCount increment perfectly
+// in sync, because they can diverge in the presence of OS-triggered paints or system load.
+// Instead, wait for a minimum number of afterPaint events to at least ensure that they are being fired.
+const minimumAfterPaintsToPass = 10;
+
+function doElementFlicker() {
+ ok(true, "Element color iteration " + color +
+ ", afterpaint count: " + afterPaintCount +
+ ", mozpaint count: " + SpecialPowers.DOMWindowUtils.paintCount);
+ if (afterPaintCount >= minimumAfterPaintsToPass) {
+ ok(true, "afterPaintCount incremented enough from color changes.");
+ SimpleTest.finish();
+ return;
+ }
+
+ color = (color + 1) % 256;
+ document.getElementById("p").style.backgroundColor = "rgb(" + color + "," + color + "," + color + ")";
+ setTimeout(doElementFlicker, 0);
+}
+
+function doBackgroundFlicker() {
+ ok(true, "Background color iteration " + color +
+ ", afterpaint count: " + afterPaintCount +
+ ", mozpaint count: " + SpecialPowers.DOMWindowUtils.paintCount);
+ if (afterPaintCount >= minimumAfterPaintsToPass) {
+ ok(true, "afterPaintCount incremented enough from background color changes.");
+ afterPaintCount = 0;
+ initialPaintCount = SpecialPowers.DOMWindowUtils.paintCount;
+ doElementFlicker();
+ return;
+ }
+
+ color = (color + 1) % 256;
+ document.body.style.backgroundColor = "rgb(" + color + "," + color + "," + color + ")";
+ setTimeout(doBackgroundFlicker, 0);
+}
+
+</script>
+</pre>
+
+<div style="height:4000px"></div>
+<a id="first" href="http://www.mozilla.org/">first<br>link</a>
+<a id="second" href="http://www.mozilla.org/">second link</a>
+<a id="third" href="http://www.mozilla.org/">third<br>link</a>
+<div style="height:4000px"></div>
+
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug644768.html b/layout/base/tests/test_bug644768.html
new file mode 100644
index 0000000000..396fccb5d0
--- /dev/null
+++ b/layout/base/tests/test_bug644768.html
@@ -0,0 +1,62 @@
+<!-- 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/. -->
+
+<!DOCTYPE html>
+<html>
+ <!--
+ https://bugzilla.mozilla.org/show_bug.cgi?id=644768
+ -->
+ <head>
+ <title>Test for Bug 644768</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ </head>
+ <body onload="test()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=644768">Mozilla Bug 644768</a>
+ <p id="display"></p>
+ <div id="content">
+ <!-- test text is
+ == زادروزها ==
+ * [[۱۳۰۷]]
+ -->
+ <textarea id="testInput" dir="rtl" cols="80" rows="25" style="-moz-appearance:none">
+
+== &#x0632;&#x0627;&#x062F;&#x0631;&#x0648;&#x0632;&#x0647;&#x0627; ==
+* [[&#x06F1;&#x06F3;&#x06F0;&#x06F7;]]</textarea>
+ </div>
+ <pre id="test">
+ <script class="testbody" type="text/javascript">
+
+ /** Test for Bug 644768 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function test() {
+ var textInput = $("testInput");
+ var s1, s2, equal, str1, str2;
+
+ textInput.focus();
+ s1 = snapshotWindow(window);
+
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_ArrowUp");
+ synthesizeKey("KEY_Delete");
+ synthesizeKey("KEY_Enter");
+ // Bug 1016184: Touch caret will hide due to key event.
+ s2 = snapshotWindow(window);
+
+ [equal, str1, str2] = compareSnapshots(s1, s2, true);
+ ok(equal, "newline before bidi text shouldn't change direction: expected " +
+ str1 + " but got " + str2);
+
+ SimpleTest.finish();
+ }
+
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug646757.html b/layout/base/tests/test_bug646757.html
new file mode 100644
index 0000000000..b941643c29
--- /dev/null
+++ b/layout/base/tests/test_bug646757.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=646757
+-->
+<head>
+ <title>Test for Bug 646757</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body style="margin:0" id="body">
+<div style="height:20.3px; width:400px; background:pink" id="d1"></div>
+<div style="height:20px; width:400px; background:yellow" id="d2"></div>
+<div style="height:9.7px; width:400px;" id="space1"></div>
+<div style="height:20.7px; width:400px; background:pink" id="d3"></div>
+<div style="height:20px; width:400px; background:yellow" id="d4"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=646757">Mozilla Bug 646757</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+function testPoint(x, y, id) {
+ is(document.elementFromPoint(x, y).id, id,
+ "checking element at " + x + "," + y);
+}
+
+/** Test for Bug 646757 **/
+testPoint(200, 20, "d1");
+testPoint(200, 20.2, "d1");
+testPoint(200, 20.4, "d2");
+testPoint(200, 21, "d2");
+
+testPoint(200, 70, "d3");
+testPoint(200, 70.6, "d3");
+testPoint(200, 70.8, "d4");
+testPoint(200, 71, "d4");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug66619.html b/layout/base/tests/test_bug66619.html
new file mode 100644
index 0000000000..064a8bf277
--- /dev/null
+++ b/layout/base/tests/test_bug66619.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=66619
+-->
+<head>
+ <title>Test for Bug 66619</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=66619">Mozilla Bug 66619</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 66619 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run()
+{
+ is(window.scrollY, 0, "window should initially be at top");
+
+ document.getElementById("first").focus();
+ var first = window.scrollY;
+ isnot(first, 0, "we scrolled to first anchor");
+ ok(first + window.innerHeight > 4000,
+ "we scrolled enough to show the anchor");
+
+ window.scrollTo(0, 0);
+ document.getElementById("second").focus();
+ var second = window.scrollY;
+
+ window.scrollTo(0, 0);
+ document.getElementById("third").focus();
+ var third = window.scrollY;
+
+ is(second, first, "we scrolled the second line of the anchor into view");
+ isnot(third, second, "we scrolled the second line of the anchor into view");
+ ok(third > second, "we scrolled the second line of the anchor into view");
+
+ window.scrollTo(0, 0); // make the results visible
+ SimpleTest.finish();
+}
+
+
+</script>
+</pre>
+
+<div style="height:4000px"></div>
+<a id="first" href="http://www.mozilla.org/">first<br>link</a>
+<a id="second" href="http://www.mozilla.org/">second link</a>
+<a id="third" href="http://www.mozilla.org/">third<br>link</a>
+<div style="height:4000px"></div>
+
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug667512.html b/layout/base/tests/test_bug667512.html
new file mode 100644
index 0000000000..fa8eb78dbf
--- /dev/null
+++ b/layout/base/tests/test_bug667512.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=667512
+-->
+<head>
+ <title>Test for Bug 667512</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<table contenteditable="true"><tbody><tr><td id="b"><br id="a"></td></tr></tbody></table>
+<span style="display: list-item;direction: rtl;"></span>
+<script type="application/javascript">
+
+/** Test for Bug 667512 **/
+function appendElements() {
+ window.focus();
+ window.getSelection().collapse(document.documentElement, 0);
+
+ var x=document.getElementById('a');
+ x.remove();
+
+ var x=document.getElementById('b');
+ x.remove();
+
+ synthesizeKey("KEY_ArrowLeft");
+ synthesizeKey("KEY_ArrowRight");
+
+ ok(true, "Should not crash!");
+ SimpleTest.finish();
+}
+
+addLoadEvent(appendElements);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug677878.html b/layout/base/tests/test_bug677878.html
new file mode 100644
index 0000000000..8dbb624d22
--- /dev/null
+++ b/layout/base/tests/test_bug677878.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=677878
+-->
+<head>
+ <title>Test for Bug 677878</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+ <style>
+ #test1 {
+ background: green;
+ height: 100px;
+ width: 100px;
+ -moz-transform: scale(20, 20);
+ -moz-transform-origin: 0 0%;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677878">Mozilla Bug 677878</a>
+<div id="content">
+ <div id="test1">
+ <div id="test2" onclick="testFinish();">
+ test
+ </div>
+ </div>
+
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+runtests();
+
+function runtests() {
+ function doClick() {
+ document.getElementById("test2").addEventListener("mousedown", testFinish, true);
+ // Don't target the center because the center could actually be outside the
+ // viewport.
+ synthesizeMouse(document.getElementById("test2"), 10, 10, { type: "mousedown" })
+ }
+ setTimeout(doClick, 300);
+}
+
+function testFinish(event){
+ ok(true, "We can still interact with the item after it is transformed");
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug687297.html b/layout/base/tests/test_bug687297.html
new file mode 100644
index 0000000000..3395bcf9cc
--- /dev/null
+++ b/layout/base/tests/test_bug687297.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=687297
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 687297</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/SpecialPowers.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=687297">Mozilla Bug 687297</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+ <script class="testbody" type="text/javascript">
+ /** Test for Bug 687297 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ var size_a=0, size_b=0, size_c=0;
+
+ window.report_size_a = function(s) {
+ size_a = s;
+ };
+
+ window.report_size_b = function(s) {
+ size_b = s;
+ };
+
+ window.report_size_c = function(s) {
+ size_c = s;
+
+ isnot(size_a, size_b, "Font sizes are changing with global language-specific minimum font size");
+ is(size_c, size_a, "Font sizes are equal, propagating only the presentation-level base minimum font size");
+
+ SimpleTest.finish();
+ };
+
+ SpecialPowers.pushPrefEnv(
+ {'set':[["font.minimum-size.ja", 120]]},
+ function() {
+ window.open("bug687297_a.html", '_blank');
+ }
+ );
+
+ </script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug696020.html b/layout/base/tests/test_bug696020.html
new file mode 100644
index 0000000000..01273c0708
--- /dev/null
+++ b/layout/base/tests/test_bug696020.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=696020
+-->
+<head>
+ <title>Test for Bug 696020</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=696020">Mozilla Bug 696020</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 696020 **/
+
+
+function startTest() {
+ var testFrame = document.getElementById("tf").contentWindow;
+ testFrame.focus();
+ var didHandleKeyEvent = false;
+ testFrame.addEventListener("keypress",
+ function(e) {
+ is(e.target, testFrame.document.body,
+ "Body element should be event target for key events!");
+ didHandleKeyEvent = true;
+ });
+ synthesizeKey("A", {}, testFrame);
+ ok(didHandleKeyEvent, "Should have handled a key event!");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(startTest)
+
+
+</script>
+</pre>
+<iframe id="tf"></iframe>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug718809.html b/layout/base/tests/test_bug718809.html
new file mode 100644
index 0000000000..a27d7c0e57
--- /dev/null
+++ b/layout/base/tests/test_bug718809.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=718809
+-->
+<head>
+ <title>Test for Bug 718809</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+
+</head>
+<body style=margin:0>
+<div style="background:blue;height:50px;width:100px; -moz-transform: matrix3d(1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 10, 1); -moz-transform-origin:0 0"></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=718809">Mozilla Bug 718809</a>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+ var rect = document.querySelector("div").getBoundingClientRect();
+
+ is(rect.top, 0, "Incorrect bounding rect");
+ is(rect.left, 0, "Incorrect bounding rect");
+ is(rect.right, 100, "Incorrect bounding rect");
+ is(rect.bottom, 50, "Incorrect bounding rect");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/layout/base/tests/test_bug725426.html b/layout/base/tests/test_bug725426.html
new file mode 100644
index 0000000000..f020303627
--- /dev/null
+++ b/layout/base/tests/test_bug725426.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=725426
+-->
+<title>Test for bug 725426</title>
+<script src=/tests/SimpleTest/SimpleTest.js></script>
+<link rel=stylesheet href=/tests/SimpleTest/test.css>
+<body style=margin:0>
+<div style="-moz-transform: perspective(200px)">
+<div style="-moz-transform: translatez(-100px);
+width:100px;height:100px;background:blue">
+</div></div>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=725426">
+Mozilla Bug 725426</a>
+<pre id=test>
+<script class=testbody>
+var rect = document.querySelector("div>div").getBoundingClientRect();
+is(rect.top, 0, "Incorrect bounding rect top");
+is(rect.right, 100, "Incorrect bounding rect top");
+is(rect.bottom, 100, "Incorrect bounding rect top");
+is(rect.left, 0, "Incorrect bounding rect top");
+</script>
+</pre>
diff --git a/layout/base/tests/test_bug731777.html b/layout/base/tests/test_bug731777.html
new file mode 100644
index 0000000000..893ad92842
--- /dev/null
+++ b/layout/base/tests/test_bug731777.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 731777</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #container {
+ position: relative;
+ height: 300px;
+ width: 300px;
+ margin: 50px 100px;
+ border: 2px solid blue;
+ background-color: #044B0A;
+
+ -moz-perspective: 500px;
+ overflow:hidden;
+ }
+
+ #inner {
+ margin: 0px;
+ width: 480px;
+ border: 2px solid blue;
+ height: 220px;
+ background-color: #844BCA;
+
+ -moz-transform: rotateY(91deg) translateX(0px) translateZ(0px);
+ transition: 5s;
+ }
+
+ </style>
+</head>
+<body>
+<div id="container">
+ <div id="inner"></div>
+</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 731777 **/
+
+is(document.elementFromPoint(325,170), document.getElementById("inner"), "Able to hit transformed object");
+is(document.elementFromPoint(405,170), document.getElementById("inner"), "Able to hit transformed object");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug749186.html b/layout/base/tests/test_bug749186.html
new file mode 100644
index 0000000000..fed00c36f6
--- /dev/null
+++ b/layout/base/tests/test_bug749186.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=749186
+Note that this is a crashtest, but because of the special privileges
+required, it needs to be run as a mochitest. Thus, the expected
+behavior of this test is that it actually loads and doesn't crash the
+browser.
+-->
+ <head>
+ <title>Test for Bug 749186 (Crashtest)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script>
+ function endTest() {
+ ok(true, 'test finished without crashing');
+ SimpleTest.finish();
+ }
+
+ function removeBoldStyle() {
+ document.getElementById('b').removeAttribute('style');
+ SpecialPowers.pushPrefEnv({'set': [['font.size.inflation.emPerLine', 0]]},endTest);
+ }
+
+ function startTest() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ SpecialPowers.pushPrefEnv({'set': [['font.size.inflation.emPerLine', 8]]},removeBoldStyle);
+ }
+
+ startTest();
+ </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=749186">Bug 749186</a>
+ <iframe id="a" style="display: none;"></iframe>
+ <div id="b" style="display: inline;"></div>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug761572.html b/layout/base/tests/test_bug761572.html
new file mode 100644
index 0000000000..250c9b559e
--- /dev/null
+++ b/layout/base/tests/test_bug761572.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=761572
+-->
+<head>
+ <title>Test for Bug 761572</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=761572">Mozilla Bug 761572</a>
+<div id="content">
+ <div id="d" style="background:lime; width:50px; height:50px" onmouseup="doUp()" onclick="doClick()"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var d = document.getElementById("d");
+
+function doUp() {
+ d.style.display = "none";
+}
+function doClick() {
+ ok(true, "Check click received");
+ SimpleTest.finish();
+}
+
+function doTest() {
+ // synthesizes a mousedown/mouseup pair
+ synthesizeMouse(d, 10, 10, {});
+}
+
+SimpleTest.waitForFocus(doTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug770106.html b/layout/base/tests/test_bug770106.html
new file mode 100644
index 0000000000..dc969b6095
--- /dev/null
+++ b/layout/base/tests/test_bug770106.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 770106</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<span id="s">Hello</span>
+<button><div style="pointer-events:none; position:relative; width:100px; background:yellow; left:-100px;">Kitty</div></button>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 770106 **/
+
+var sRect = s.getBoundingClientRect();
+is(document.elementFromPoint(sRect.left + sRect.width/2, sRect.top + sRect.height/2),
+ document.getElementById("s"), "Correct object selected");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug842853-2.html b/layout/base/tests/test_bug842853-2.html
new file mode 100644
index 0000000000..ee30fb8e93
--- /dev/null
+++ b/layout/base/tests/test_bug842853-2.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=842853
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 842853</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 842853 **/
+
+SimpleTest.waitForExplicitFinish();
+
+async function verifyAfterLoad() {
+ var e = document.getElementsByTagName('iframe')[0];
+ var win = e.contentWindow;
+ if (win.location.hash != '') {
+ // Allow a half pixel difference because the scroll position is aligned with
+ // screen pixels instead of CSS pixels (bug 1774315).
+ isfuzzy(win.scrollY, 500, 0.5);
+ SimpleTest.finish();
+ return;
+ }
+}
+
+function runTest() {
+ var e = document.getElementsByTagName('iframe')[0];
+ var win = e.contentWindow;
+ if (win.location.hash != '') {
+ return;
+ }
+ win.location.hash='#anchor'
+ win.scrollTo(0,500);
+ e.setAttribute("onload","verifyAfterLoad()");
+ win.location.reload()
+}
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=842853">Mozilla Bug 842853</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<iframe src="file_bug842853.html"></iframe>
+<script>
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug842853.html b/layout/base/tests/test_bug842853.html
new file mode 100644
index 0000000000..c6b5f40e95
--- /dev/null
+++ b/layout/base/tests/test_bug842853.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=842853
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 842853</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 842853 **/
+
+SimpleTest.waitForExplicitFinish();
+
+async function runTest() {
+ var win = e.contentWindow;
+ if (win.location.hash != '') {
+ // Allow a half pixel difference because the scroll position is aligned with
+ // screen pixels instead of CSS pixels (bug 1774315).
+ isfuzzy(win.scrollY, 500, 0.5);
+ SimpleTest.finish();
+ return;
+ }
+ win.location.hash='#anchor'
+ win.scrollTo(0,500);
+ win.location.reload()
+}
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=842853">Mozilla Bug 842853</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script>
+
+var e = document.createElement('iframe');
+var url = 'file_bug842853-frame.html';
+e.setAttribute('src',url);
+e.setAttribute('onload','runTest()');
+document.body.appendChild(e);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug849219.html b/layout/base/tests/test_bug849219.html
new file mode 100644
index 0000000000..d116afeb78
--- /dev/null
+++ b/layout/base/tests/test_bug849219.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=849219
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 849219</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 849219 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ var win = e.contentWindow;
+ if (win.location.hash != '') {
+ is(win.scrollY,0);
+ SimpleTest.finish();
+ return;
+ }
+ win.location.hash='#anchor'
+ win.scrollTo(0,0);
+ win.location.reload()
+}
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=849219">Mozilla Bug 849219</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script>
+
+var e = document.createElement('iframe');
+var url = 'file_bug842853-frame.html';
+e.setAttribute('src',url);
+e.setAttribute('onload','runTest()');
+document.body.appendChild(e);
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug851445.html b/layout/base/tests/test_bug851445.html
new file mode 100644
index 0000000000..a89ccce51c
--- /dev/null
+++ b/layout/base/tests/test_bug851445.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=851445
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 851445</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=851445">Mozilla Bug 851445</a>
+<p id="display"></p>
+<iframe id="f" style="width:400px; height:400px;"></iframe>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function handleLoad() {
+ f.contentWindow.scrollTo(0,100);
+ function handleLoad2() {
+ // Verify that the scroll position was retained
+ is(f.contentWindow.scrollY, 100);
+ SimpleTest.finish();
+ }
+ f.onload = handleLoad2;
+ f.contentWindow.location.reload();
+}
+
+f.src = "bug851445_helper.html?" + Math.random();
+f.onload = handleLoad;
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug851485.html b/layout/base/tests/test_bug851485.html
new file mode 100644
index 0000000000..a5f3488189
--- /dev/null
+++ b/layout/base/tests/test_bug851485.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=851485
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 851485</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 851485 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var testRunning = false;
+async function runTest() {
+ if (testRunning) {
+ return;
+ }
+ testRunning = true;
+
+ var e = document.getElementsByTagName('iframe')[0];
+ var win = e.contentWindow;
+
+ var p = new Promise((r) => { win.addEventListener("hashchange", r, { once: true }); });
+ win.location.hash = '#anchor';
+ await p;
+
+ win.scrollTo(0, 500);
+
+ p = new Promise((r) => { e.addEventListener("load", r, { once: true }); });
+ win.location.reload();
+ await p;
+
+ is(win.location.hash, "#anchor", "We set the location's hash to 'anchor'");
+ // Allow a half pixel difference because the scroll position is aligned with
+ // screen pixels instead of CSS pixels (bug 1774315).
+ isfuzzy(win.scrollY, 500, 0.5, "Reloading keeps scroll position");
+
+ var link = win.document.getElementsByTagName('a')[0];
+ var anchor = win.document.getElementsByTagName('a')[1];
+ p = new Promise((r) => {
+ var observer = new IntersectionObserver((entries) => {
+ info("IntersectionObserver: entering callback")
+ if (entries.some((entry) => entry.isIntersecting)) {
+ info("IntersectionObserver: some entry isIntersecting; disconnecting")
+ observer.disconnect();
+ r();
+ }
+ });
+ observer.observe(anchor);
+ });
+ win.document.body.offsetHeight;
+ synthesizeMouseAtCenter(link, {type: "mousedown"}, win);
+ synthesizeMouseAtCenter(link, {type: "mouseup"}, win);
+ info("Sending click")
+ sendMouseEvent({type: "click"}, link, win);
+ await p;
+ let actualScroll = win.scrollY;
+ info("Promise resolved (for IntersectionObserver). win.scrollY is " +
+ actualScroll);
+
+ ok(actualScroll > 3000, "Scrolling after load works.");
+
+ SimpleTest.finish();
+}
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=851485">Mozilla Bug 851485</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+</pre>
+<iframe src="file_bug842853.html"></iframe>
+<script>
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug858459.html b/layout/base/tests/test_bug858459.html
new file mode 100644
index 0000000000..c12ea94819
--- /dev/null
+++ b/layout/base/tests/test_bug858459.html
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=858459
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 858459</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 858459 **/
+
+var result = "";
+var timeout = null;
+var clicks = 0;
+const EXPECTED_RESULT = "change select";
+
+function logEvent(ev,msg) {
+ result += ev.type + ' ' + msg;
+ ++clicks;
+ if (result.length > EXPECTED_RESULT.length)
+ finishTest();
+}
+
+document.onclick = function(event) { logEvent(event,"document"); }
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+function finishTest() {
+ if (!timeout) return;
+ clearTimeout(timeout);
+ timeout = null;
+ is(result,EXPECTED_RESULT,"");
+ SimpleTest.finish();
+}
+
+function runTest() {
+ // Need a timeout to check that an event has _not_ occurred.
+ timeout = setTimeout(finishTest, 5000);
+ synthesizeMouseAtCenter(document.getElementById('test858459'), { });
+}
+
+ </script>
+</head>
+<body onload="SimpleTest.waitForFocus(runTest)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=858459">Mozilla Bug 858459</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test"><div><select id="test858459" size=4 onclick="logEvent(event,'select');" onchange="logEvent(event,'select');var div = document.querySelector('#test div'); div.innerHTML='<p>'+div.innerHTML; document.body.offsetHeight;"><option>1111111111111111<option>2<option>3</select></div>
+
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug93077-1.html b/layout/base/tests/test_bug93077-1.html
new file mode 100644
index 0000000000..780f9c24a3
--- /dev/null
+++ b/layout/base/tests/test_bug93077-1.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=93077
+-->
+<head>
+ <title>Test for Bug 93077</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #filler { height: 200cm; background: papayawhip; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=93077">Mozilla Bug 93077</a>
+<p id="display"></p>
+<div id=filler>...</div>
+<p id=below></p>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 93077 **/
+["#top", "#TOP", "#Top"].forEach(function(fragid) {
+ document.getElementById("below").scrollIntoView()
+ isnot(window.scrollY, 0)
+ location.hash = fragid
+ is(window.scrollY, 0)
+})
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug93077-2.html b/layout/base/tests/test_bug93077-2.html
new file mode 100644
index 0000000000..7985d63ad1
--- /dev/null
+++ b/layout/base/tests/test_bug93077-2.html
@@ -0,0 +1,31 @@
+<!-- Testing quirks mode. -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=93077
+-->
+<head>
+ <title>Test for Bug 93077</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #filler { height: 200cm; background: papayawhip; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=93077">Mozilla Bug 93077</a>
+<p id="display"></p>
+<div id=filler>...</div>
+<p id=below></p>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 93077 **/
+["#top", "#TOP", "#Top"].forEach(function(fragid) {
+ document.getElementById("below").scrollIntoView()
+ isnot(window.scrollY, 0)
+ location.hash = fragid
+ is(window.scrollY, 0)
+})
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug93077-3.html b/layout/base/tests/test_bug93077-3.html
new file mode 100644
index 0000000000..357a18cb5e
--- /dev/null
+++ b/layout/base/tests/test_bug93077-3.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=93077
+-->
+<head>
+ <title>Test for Bug 93077</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #filler { height: 200cm; background: papayawhip; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=93077">Mozilla Bug 93077</a>
+<p id="display"></p>
+<div id=filler>...</div>
+<p id=below></p>
+<p id=top>Top</p>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 93077 **/
+["#TOP", "#Top"].forEach(function(fragid) {
+ document.getElementById("below").scrollIntoView()
+ isnot(window.scrollY, 0)
+ location.hash = fragid
+ is(window.scrollY, 0)
+})
+location.hash = "#top"
+isnot(window.scrollY, 0)
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug93077-4.html b/layout/base/tests/test_bug93077-4.html
new file mode 100644
index 0000000000..c0a16596b5
--- /dev/null
+++ b/layout/base/tests/test_bug93077-4.html
@@ -0,0 +1,34 @@
+<!-- Testing quirks mode. -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=93077
+-->
+<head>
+ <title>Test for Bug 93077</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #filler { height: 200cm; background: papayawhip; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=93077">Mozilla Bug 93077</a>
+<p id="display"></p>
+<div id=filler>...</div>
+<p id=below></p>
+<p><a name=top>Top</a></p>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 93077 **/
+["#TOP", "#Top"].forEach(function(fragid) {
+ document.getElementById("below").scrollIntoView()
+ isnot(window.scrollY, 0)
+ location.hash = fragid
+ is(window.scrollY, 0)
+})
+location.hash = "#top"
+isnot(window.scrollY, 0)
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug93077-5.html b/layout/base/tests/test_bug93077-5.html
new file mode 100644
index 0000000000..b2701a392c
--- /dev/null
+++ b/layout/base/tests/test_bug93077-5.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=93077
+-->
+<head>
+ <title>Test for Bug 93077</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #filler { height: 200cm; background: papayawhip; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=93077">Mozilla Bug 93077</a>
+<p id="display"></p>
+<div id=filler>...</div>
+<p id=below></p>
+<p id=TOP>Top</p>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 93077 **/
+["#top", "#Top"].forEach(function(fragid) {
+ document.getElementById("below").scrollIntoView()
+ isnot(window.scrollY, 0)
+ location.hash = fragid
+ is(window.scrollY, 0)
+})
+location.hash = "#TOP"
+isnot(window.scrollY, 0)
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug93077-6.html b/layout/base/tests/test_bug93077-6.html
new file mode 100644
index 0000000000..906ff13e2f
--- /dev/null
+++ b/layout/base/tests/test_bug93077-6.html
@@ -0,0 +1,34 @@
+<!-- Testing quirks mode. -->
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=93077
+-->
+<head>
+ <title>Test for Bug 93077</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #filler { height: 200cm; background: papayawhip; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=93077">Mozilla Bug 93077</a>
+<p id="display"></p>
+<div id=filler>...</div>
+<p id=below></p>
+<p><a name=TOP>Top</a></p>
+<pre id="test">
+<script type="application/javascript">
+/** Test for Bug 93077 **/
+["#top", "#Top"].forEach(function(fragid) {
+ document.getElementById("below").scrollIntoView()
+ isnot(window.scrollY, 0)
+ location.hash = fragid
+ is(window.scrollY, 0)
+})
+location.hash = "#TOP"
+isnot(window.scrollY, 0)
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_bug970964.html b/layout/base/tests/test_bug970964.html
new file mode 100644
index 0000000000..9f1e94c841
--- /dev/null
+++ b/layout/base/tests/test_bug970964.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=970964
+-->
+<head>
+ <title>Test for Bug 970964</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ function testWithoutImplicitPointerCapture() {
+ var iframe = document.getElementById("testFrame");
+ iframe.src = "bug970964_inner.html";
+ }
+
+ function testWithImplicitPointerCapture() {
+ var iframe = document.getElementById("testFrame");
+ iframe.src = "bug970964_inner2.html";
+ }
+
+ function runTest() {
+ SimpleTest.waitForExplicitFinish();
+ window.addEventListener("message", (event) => {
+ if (event.data == "finishTest") {
+ SimpleTest.finish();
+ } else if (event.data == "run next") {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.w3c_pointer_events.implicit_capture", true]
+ ]
+ }, testWithImplicitPointerCapture);
+ }
+ });
+
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.w3c_pointer_events.implicit_capture", false]
+ ]
+ }, testWithoutImplicitPointerCapture);
+ }
+ </script>
+</head>
+<body onload="runTest();">
+ <iframe id="testFrame" height="500" width="500"></iframe>
+</body>
+
diff --git a/layout/base/tests/test_bug977003.html b/layout/base/tests/test_bug977003.html
new file mode 100644
index 0000000000..426ac52566
--- /dev/null
+++ b/layout/base/tests/test_bug977003.html
@@ -0,0 +1,32 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=977003
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 977003</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ var number = 0;
+ var iframe = undefined;
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ iframe = document.getElementById("testFrame");
+ SimpleTest.executeSoon(finishTest);
+ }
+ function finishTest() {
+ // Try to run several tests named as bug977003_inner_<number>.html
+ if(++number < 7)
+ iframe.src = "bug977003_inner_" + number + ".html";
+ else
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/layout/base/tests/test_bug990340.html b/layout/base/tests/test_bug990340.html
new file mode 100644
index 0000000000..dea6bb346c
--- /dev/null
+++ b/layout/base/tests/test_bug990340.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=990340
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 990340</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 990340 **/
+
+ function testbug990340() {
+ ok(document.querySelector('#bug990340 span').clientHeight < 100,
+ "'height' is in transition")
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=990340">Mozilla Bug 990340</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<style type="text/css">
+
+#bug990340::before {
+ content: ":before";
+}
+
+#bug990340 span {
+ display: inline-block;
+ border:1px solid blue;
+ height: 20px;
+ transition: height 30s;
+}
+
+#bug990340.s span {
+ height: 100px;
+}
+</style>
+<div id="bug990340" style="overflow:scroll">
+ <span>Transition height</span>
+</div>
+</pre>
+
+<script>
+document.body.offsetHeight;
+document.querySelector('#bug990340').className='s';
+testbug990340()
+</script>
+
+</body>
+</html>
diff --git a/layout/base/tests/test_bug993936.html b/layout/base/tests/test_bug993936.html
new file mode 100644
index 0000000000..5325b66a22
--- /dev/null
+++ b/layout/base/tests/test_bug993936.html
@@ -0,0 +1,164 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=993936
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 993936</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 993936 **/
+
+var currentId = 0;
+var evictedTouchesCount = 0;
+
+function testtouch(aOptions) {
+ if (!aOptions)
+ aOptions = {};
+ this.identifier = aOptions.identifier || 0;
+ this.target = aOptions.target || 0;
+ this.page = aOptions.page || {x: 0, y: 0};
+ this.radius = aOptions.radius || {x: 0, y: 0};
+ this.rotationAngle = aOptions.rotationAngle || 0;
+ this.force = aOptions.force || 1;
+}
+
+function touchEvent(aOptions) {
+ if (!aOptions) {
+ aOptions = {};
+ }
+ this.ctrlKey = aOptions.ctrlKey || false;
+ this.altKey = aOptions.altKey || false;
+ this.shiftKey = aOptions.shiftKey || false;
+ this.metaKey = aOptions.metaKey || false;
+ this.touches = aOptions.touches || [];
+ this.targetTouches = aOptions.targetTouches || [];
+ this.changedTouches = aOptions.changedTouches || [];
+}
+
+function sendTouchEvent(windowUtils, aType, aEvent, aModifiers) {
+ var ids = [], xs=[], ys=[], rxs = [], rys = [],
+ rotations = [], forces = [], tiltXs = [], tiltYs = [], twists = [];
+
+ for (var touchType of ["touches", "changedTouches", "targetTouches"]) {
+ for (var i = 0; i < aEvent[touchType].length; i++) {
+ if (!ids.includes(aEvent[touchType][i].identifier)) {
+ ids.push(aEvent[touchType][i].identifier);
+ xs.push(aEvent[touchType][i].page.x);
+ ys.push(aEvent[touchType][i].page.y);
+ rxs.push(aEvent[touchType][i].radius.x);
+ rys.push(aEvent[touchType][i].radius.y);
+ rotations.push(aEvent[touchType][i].rotationAngle);
+ forces.push(aEvent[touchType][i].force);
+ tiltXs.push(0);
+ tiltYs.push(0);
+ twists.push(0);
+ }
+ }
+ }
+ return windowUtils.sendTouchEvent(aType,
+ ids, xs, ys, rxs, rys,
+ rotations, forces, tiltXs, tiltYs, twists,
+ aModifiers, 0);
+}
+
+function getSingleTouchEventForTarget(target, cwu) {
+ currentId++;
+ var bcr = target.getBoundingClientRect();
+ var touch = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target: target,
+ identifier: currentId,
+ });
+ var event = new touchEvent({
+ touches: [touch],
+ targetTouches: [touch],
+ changedTouches: [touch]
+ });
+ return event;
+}
+
+function getMultiTouchEventForTarget(target, cwu) {
+ currentId++;
+ var bcr = target.getBoundingClientRect();
+ var touch1 = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target: target,
+ identifier: currentId,
+ });
+ currentId++;
+ var touch2 = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width),
+ y: Math.round(bcr.top + bcr.height)},
+ target: target,
+ identifier: currentId,
+ });
+ var event = new touchEvent({
+ touches: [touch1, touch2],
+ targetTouches: [touch1, touch2],
+ changedTouches: [touch1, touch2]
+ });
+ return event;
+}
+
+function runTests() {
+ var cwu = SpecialPowers.getDOMWindowUtils(window);
+
+ var event1 = getMultiTouchEventForTarget(d0, cwu);
+ sendTouchEvent(cwu, "touchstart", event1, 0);
+ sendTouchEvent(cwu, "touchmove", event1, 0);
+ is(evictedTouchesCount, 0, "Still no evicted touches");
+
+ var event2 = getSingleTouchEventForTarget(d0, cwu);
+ sendTouchEvent(cwu, "touchstart", event2, 0);
+
+ // By now we should get touchend event
+ ok(evictedTouchesCount > 0, "Got evicted touch");
+
+ finishTest();
+}
+
+function finishTest() {
+ // Let window.onerror have a chance to fire
+ setTimeout(function() {
+ SimpleTest.finish();
+ }, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=993936">Mozilla Bug 993936</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<div id="d0">
+ Test div
+</div>
+
+<script>
+var d0 = document.getElementById("d0");
+
+d0.addEventListener("touchend", function(ev) {
+ evictedTouchesCount++;
+});
+
+window.onload = function () {
+ setTimeout(function() {
+ runTests();
+ }, 0);
+}
+
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_caret_browsing_around_form_controls.html b/layout/base/tests/test_caret_browsing_around_form_controls.html
new file mode 100644
index 0000000000..8b4fb480cd
--- /dev/null
+++ b/layout/base/tests/test_caret_browsing_around_form_controls.html
@@ -0,0 +1,379 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ await SpecialPowers.pushPrefEnv({
+ set:[
+ ["accessibility.browsewithcaret", true],
+ ],
+ });
+ (function test_move_caret_from_before_input_text() {
+ info("Starting test_move_caret_from_before_input_text...");
+ document.body.innerHTML = '<div>abc<input value="def">ghi</div>';
+ const input = document.querySelector("input");
+ const textBefore = input.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_input_text: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ input.nextSibling,
+ "test_move_caret_from_before_input_text: caret should be moved to text node after the <input>"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_input_text: caret should be moved to start of the text node"
+ );
+ })();
+ (function test_move_caret_from_after_input_text() {
+ info("Starting test_move_caret_from_after_input_text...");
+ document.body.innerHTML = '<div>abc<input value="def">ghi</div>';
+ const input = document.querySelector("input");
+ getSelection().collapse(input.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_input_text: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ input.previousSibling,
+ "test_move_caret_from_after_input_text: caret should be moved to text node before the <input>"
+ );
+ is(
+ getSelection().focusOffset,
+ input.previousSibling.length,
+ "test_move_caret_from_after_input_text: caret should be moved to end of the text node"
+ );
+ })();
+ (function test_move_caret_from_before_double_input_text() {
+ info("Starting test_move_caret_from_before_double_input_text...");
+ document.body.innerHTML = '<div>abc<input value="def"><input value="ghi">jkl</div>';
+ const firstInput = document.querySelector("input");
+ const secondInput = firstInput.nextSibling;
+ const textBefore = firstInput.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_double_input_text: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ secondInput.nextSibling,
+ "test_move_caret_from_before_double_input_text: caret should be moved to text node after the second <input> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_double_input_text: caret should be moved to text node after the second <input> (offset)"
+ );
+ })();
+ (function test_move_caret_from_after_double_input_text() {
+ info("Starting test_move_caret_from_after_double_input_text...");
+ document.body.innerHTML = '<div>abc<input value="def"><input value="ghi">jkl</div>';
+ const firstInput = document.querySelector("input");
+ const secondInput = firstInput.nextSibling;
+ getSelection().collapse(secondInput.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_double_input_text: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ firstInput.previousSibling,
+ "test_move_caret_from_after_double_input_text: caret should be moved to text node before the first <input> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ firstInput.previousSibling.length,
+ "test_move_caret_from_after_double_input_text: caret should be moved to text node before the first <input> (offset)"
+ );
+ })();
+
+ (function test_move_caret_from_before_input_button() {
+ info("Starting test_move_caret_from_before_input_button...");
+ document.body.innerHTML = '<div>abc<input type="button" value="def">ghi</div>';
+ const input = document.querySelector("input");
+ const textBefore = input.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_input_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ input.nextSibling,
+ "test_move_caret_from_before_input_button: caret should be moved to text node after the <input>"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_input_button: caret should be moved to start of the text node"
+ );
+ })();
+ (function test_move_caret_from_after_input_button() {
+ info("Starting test_move_caret_from_after_input_button...");
+ document.body.innerHTML = '<div>abc<input type="button" value="def">ghi</div>';
+ const input = document.querySelector("input");
+ getSelection().collapse(input.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_input_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ input.previousSibling,
+ "test_move_caret_from_after_input_button: caret should be moved to text node before the <input>"
+ );
+ is(
+ getSelection().focusOffset,
+ input.previousSibling.length,
+ "test_move_caret_from_after_input_button: caret should be moved to end of the text node"
+ );
+ })();
+ (function test_move_caret_from_before_double_input_button() {
+ info("Starting test_move_caret_from_before_double_input_button...");
+ document.body.innerHTML = '<div>abc<input type="button" value="def"><input type="button" value="ghi">jkl</div>';
+ const firstInput = document.querySelector("input");
+ const secondInput = firstInput.nextSibling;
+ const textBefore = firstInput.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_double_input_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ secondInput.nextSibling,
+ "test_move_caret_from_before_double_input_button: caret should be moved to text node after the second <input> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_double_input_button: caret should be moved to text node after the second <input> (offset)"
+ );
+ })();
+ (function test_move_caret_from_after_double_input_button() {
+ info("Starting test_move_caret_from_after_double_input_button...");
+ document.body.innerHTML = '<div>abc<input type="button" value="def"><input type="button" value="ghi">jkl</div>';
+ const firstInput = document.querySelector("input");
+ const secondInput = firstInput.nextSibling;
+ getSelection().collapse(secondInput.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_double_input_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ firstInput.previousSibling,
+ "test_move_caret_from_after_double_input_button: caret should be moved to text node before the first <input> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ firstInput.previousSibling.length,
+ "test_move_caret_from_after_double_input_button: caret should be moved to text node before the first <input> (offset)"
+ );
+ })();
+
+ (function test_move_caret_from_before_button() {
+ info("Starting test_move_caret_from_before_button...");
+ document.body.innerHTML = '<div>abc<button>def</button>ghi</div>';
+ const button = document.querySelector("button");
+ const textBefore = button.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ button.nextSibling,
+ "test_move_caret_from_before_button: caret should be moved to text node after the <button>"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_button: caret should be moved to start of the text node"
+ );
+ })();
+ (function test_move_caret_from_after_button() {
+ info("Starting test_move_caret_from_after_button...");
+ document.body.innerHTML = '<div>abc<button>def</button>ghi</div>';
+ const button = document.querySelector("button");
+ getSelection().collapse(button.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ button.previousSibling,
+ "test_move_caret_from_after_button: caret should be moved to text node before the <button>"
+ );
+ is(
+ getSelection().focusOffset,
+ button.previousSibling.length,
+ "test_move_caret_from_after_button: caret should be moved to end of the text node"
+ );
+ })();
+ (function test_move_caret_from_before_double_button() {
+ info("Starting test_move_caret_from_before_double_button...");
+ document.body.innerHTML = '<div>abc<button>def</button><button>ghi</button>jkl</div>';
+ const firstButton = document.querySelector("button");
+ const secondButton = firstButton.nextSibling;
+ const textBefore = firstButton.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_double_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ secondButton.nextSibling,
+ "test_move_caret_from_before_double_button: caret should be moved to text node after the second <button> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_double_button: caret should be moved to text node after the second <button> (offset)"
+ );
+ })();
+ (function test_move_caret_from_after_double_button() {
+ info("Starting test_move_caret_from_after_double_button...");
+ document.body.innerHTML = '<div>abc<button>def</button><button>ghi</button>jkl</div>';
+ const firstButton = document.querySelector("button");
+ const secondButton = firstButton.nextSibling;
+ getSelection().collapse(secondButton.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_double_button: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ firstButton.previousSibling,
+ "test_move_caret_from_after_double_button: caret should be moved to text node before the first <button> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ firstButton.previousSibling.length,
+ "test_move_caret_from_after_double_button: caret should be moved to text node before the first <button> (offset)"
+ );
+ })();
+
+ (function test_move_caret_from_before_textarea() {
+ info("Starting test_move_caret_from_before_textarea...");
+ document.body.innerHTML = '<div>abc<textarea>def</textarea>ghi</div>';
+ const textarea = document.querySelector("textarea");
+ const textBefore = textarea.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_textarea: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ textarea.nextSibling,
+ "test_move_caret_from_before_textarea: caret should be moved to text node after the <textarea>"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_textarea: caret should be moved to start of the text node"
+ );
+ })();
+ (function test_move_caret_from_after_textarea() {
+ info("Starting test_move_caret_from_after_textarea...");
+ document.body.innerHTML = '<div>abc<textarea>def</textarea>ghi</div>';
+ const textarea = document.querySelector("textarea");
+ getSelection().collapse(textarea.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_textarea: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ textarea.previousSibling,
+ "test_move_caret_from_after_textarea: caret should be moved to text node before the <textarea>"
+ );
+ is(
+ getSelection().focusOffset,
+ textarea.previousSibling.length,
+ "test_move_caret_from_after_textarea: caret should be moved to end of the text node"
+ );
+ })();
+ (function test_move_caret_from_before_double_textarea() {
+ info("Starting test_move_caret_from_before_double_textarea...");
+ document.body.innerHTML = '<div>abc<textarea>def</textarea><textarea>ghi</textarea>jkl</div>';
+ const firstTextarea = document.querySelector("textarea");
+ const secondTextarea = firstTextarea.nextSibling;
+ const textBefore = firstTextarea.previousSibling;
+ getSelection().collapse(textBefore, textBefore.length);
+ synthesizeKey("KEY_ArrowRight");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_before_double_textarea: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ secondTextarea.nextSibling,
+ "test_move_caret_from_before_double_textarea: caret should be moved to text node after the second <textarea> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ 0,
+ "test_move_caret_from_before_double_textarea: caret should be moved to text node after the second <textarea> (offset)"
+ );
+ })();
+ (function test_move_caret_from_after_double_textarea() {
+ info("Starting test_move_caret_from_after_double_textarea...");
+ document.body.innerHTML = '<div>abc<textarea>def</textarea><textarea>ghi</textarea>jkl</div>';
+ const firstTextarea = document.querySelector("textarea");
+ const secondTextarea = firstTextarea.nextSibling;
+ getSelection().collapse(secondTextarea.nextSibling, 0);
+ synthesizeKey("KEY_ArrowLeft");
+ ok(
+ getSelection().isCollapsed,
+ "test_move_caret_from_after_double_textarea: selection should be collapsed after moving caret"
+ );
+ is(
+ getSelection().focusNode,
+ firstTextarea.previousSibling,
+ "test_move_caret_from_after_double_textarea: caret should be moved to text node before the first <textarea> (container)"
+ );
+ is(
+ getSelection().focusOffset,
+ firstTextarea.previousSibling.length,
+ "test_move_caret_from_after_double_textarea: caret should be moved to text node before the first <textarea> (offset)"
+ );
+ })();
+
+ SimpleTest.finish();
+});
+</script>
+</head>
+<body></body>
+</html>
diff --git a/layout/base/tests/test_dynamic_toolbar_max_height.html b/layout/base/tests/test_dynamic_toolbar_max_height.html
new file mode 100644
index 0000000000..abf0c34e8e
--- /dev/null
+++ b/layout/base/tests/test_dynamic_toolbar_max_height.html
@@ -0,0 +1,22 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<div id="log"></div>
+<script>
+"use strict";
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["dom.meta-viewport.enabled", true],
+ ],
+ },
+ function() {
+ // We need to open a new window because the API to set the dynamic toolbar
+ // max height works only in the top content document.
+ window.open("file_dynamic_toolbar_max_height.html");
+ }
+);
+</script>
+</html>
diff --git a/layout/base/tests/test_emulateMedium.html b/layout/base/tests/test_emulateMedium.html
new file mode 100644
index 0000000000..535904f0c0
--- /dev/null
+++ b/layout/base/tests/test_emulateMedium.html
@@ -0,0 +1,165 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=819930
+-->
+<meta charset="utf-8">
+<title>Test for Bug 819930</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+<style>
+ @media braille {
+ body {
+ background-color: rgb(255, 255, 0);
+ }
+ }
+
+ @media embossed {
+ body {
+ background-color: rgb(210, 180, 140);
+ }
+ }
+
+ @media handheld {
+ body {
+ background-color: rgb(0, 255, 0);
+ }
+ }
+
+ @media print {
+ body {
+ background-color: rgb(0, 255, 255);
+ }
+ }
+
+ @media projection {
+ body {
+ background-color: rgb(30, 144, 255);
+ }
+ }
+
+ @media screen {
+ body {
+ background-color: green;
+ }
+ }
+
+ @media speech {
+ body {
+ background-color: rgb(192, 192, 192);
+ }
+ }
+
+ @media tty {
+ body {
+ background-color: rgb(255, 192, 203);
+ }
+ }
+
+ @media tv {
+ body {
+ background-color: rgb(75, 0, 130);
+ }
+ }
+</style>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=819930">Mozilla Bug 819930</a>
+<p id="display"></p>
+
+<div id="content" style="display: none"></div>
+
+<script>
+function waitForColorSchemeToBe(scheme) {
+ return new Promise(resolve => {
+ let mq = matchMedia(`(prefers-color-scheme: ${scheme})`);
+ if (mq.matches) {
+ resolve();
+ } else {
+ mq.addEventListener("change", resolve, { once: true });
+ }
+ });
+}
+
+add_setup(async function() {
+ // Set a dark color scheme so that we can properly test the print override.
+ await SpecialPowers.pushPrefEnv({ set: [["layout.css.prefers-color-scheme.content-override", 0]] });
+ await waitForColorSchemeToBe("dark");
+});
+
+add_task(function() {
+ let tests = [
+ {name: 'braille', value: 'rgb(255, 255, 0)'},
+ {name: 'embossed', value: 'rgb(210, 180, 140)'},
+ {name: 'handheld', value: 'rgb(0, 255, 0)'},
+ {name: 'print', value: 'rgb(0, 255, 255)'},
+ {name: 'projection', value: 'rgb(30, 144, 255)'},
+ {name: 'speech', value: 'rgb(192, 192, 192)'},
+ {name: 'tty', value: 'rgb(255, 192, 203)'},
+ {name: 'tv', value: 'rgb(75, 0, 130)'},
+ ];
+
+ let originalColor = 'rgb(0, 128, 0)';
+ let body = document.body;
+
+ let getColor = function() {
+ return window.getComputedStyle(body).backgroundColor;
+ };
+
+ for (let test of tests) {
+ // Emulate the given media
+ SpecialPowers.emulateMedium(window, test.name);
+ is(getColor(), test.value, 'emulating ' + test.name + ' produced ' +
+ 'correct rendering');
+
+ ok(matchMedia(test.name).matches, "Media matches");
+ if (test.value == "print") {
+ ok(matchMedia("(prefers-color-scheme: light)").matches, "color-scheme is overridden when emulating print");
+ }
+
+ // Do the @media screen rules get applied after ending the emulation?
+ SpecialPowers.stopEmulatingMedium(window);
+ is(getColor(), originalColor, 'Ending ' + test.name +
+ ' emulation restores style for original medium');
+ ok(!matchMedia(test.name).matches, "Media no longer matches");
+ ok(!matchMedia("(prefers-color-scheme: light)").matches, "color-scheme override should be restored");
+
+ // CSS media types are case-insensitive; we should be too.
+ SpecialPowers.emulateMedium(window, test.name.toUpperCase());
+ is(getColor(), test.value,
+ test.name + ' emulation is case-insensitive');
+ SpecialPowers.stopEmulatingMedium(window);
+ }
+
+ is(getColor(), originalColor, 'No emulation');
+
+ // Emulating screen should produce the same rendering as when there is
+ // no emulation in effect
+ SpecialPowers.emulateMedium(window, 'screen');
+ is(getColor(), originalColor, 'Emulating screen produces original rendering');
+ SpecialPowers.stopEmulatingMedium(window);
+
+ is(getColor(), originalColor, 'No emulation, shouldn\'t change');
+
+ // Screen should be case-insensitive too
+ SpecialPowers.emulateMedium(window, 'SCREEN');
+ is(getColor(), originalColor, 'screen emulation is case-insensitive');
+ SpecialPowers.stopEmulatingMedium(window);
+
+ is(getColor(), originalColor, 'No emulation, shouldn\'t change');
+
+ // An invalid parameter shouldn't fail. Given the CSS rules above,
+ // an invalid parameter should result in a different rendering from any
+ // produced thus far
+ SpecialPowers.emulateMedium(window, 'clay');
+ let invalid = getColor();
+ tests.push({name: 'screen', value: 'green'});
+ tests.forEach(function(test) {
+ isnot(invalid, test.value, 'Emulating invalid type differs from ' +
+ test.name);
+ });
+
+ SpecialPowers.stopEmulatingMedium(window);
+
+ is(getColor(), originalColor, 'No emulation, shouldn\'t change');
+})
+</script>
diff --git a/layout/base/tests/test_emulate_color_scheme.html b/layout/base/tests/test_emulate_color_scheme.html
new file mode 100644
index 0000000000..dce7171584
--- /dev/null
+++ b/layout/base/tests/test_emulate_color_scheme.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Emulation of color-scheme (bug 1570721)</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+<style>
+@media (prefers-color-scheme: light) {
+ #test { color: rgb(0, 1, 0); }
+}
+@media (prefers-color-scheme: dark) {
+ #test { color: rgb(0, 2, 0); }
+}
+</style>
+<div id="test"></div>
+<script>
+function colorId() {
+ // Gets the middle number of the rgb(0, x, 0) color.
+ let color = getComputedStyle(document.getElementById("test")).color;
+ let id = parseInt(color.split(",")[1], 10);
+ ok(id == 1 || id == 2 || id == 3, "Bogus color?");
+ return id;
+}
+
+{
+ let bc = SpecialPowers.wrap(window).browsingContext.top;
+ ok('prefersColorSchemeOverride' in bc, "API should exist");
+ is(bc.prefersColorSchemeOverride, "none", "Override shouldn't be active.");
+
+ let originalColor = colorId();
+
+ bc.prefersColorSchemeOverride = "light";
+ is(colorId(), 1, "Light emulation works");
+
+ bc.prefersColorSchemeOverride = "dark";
+ is(colorId(), 2, "Dark emulation works");
+
+ bc.prefersColorSchemeOverride = "none";
+ is(colorId(), originalColor, "Clearing the override works");
+}
+</script>
diff --git a/layout/base/tests/test_event_target_iframe_oop.html b/layout/base/tests/test_event_target_iframe_oop.html
new file mode 100644
index 0000000000..562433c955
--- /dev/null
+++ b/layout/base/tests/test_event_target_iframe_oop.html
@@ -0,0 +1,177 @@
+<!DOCTYPE HTML>
+<html id="html" style="height:100%">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=921928
+-->
+<head>
+ <title>Test for bug 921928</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #dialer {
+ position: absolute;
+ left: 0;
+ top: 0;
+ width: 100%;
+ height: 50px;
+ background: green;
+ }
+
+ #apps {
+ position: absolute;
+ left: 0;
+ top: 51px;
+ width: 100%;
+ height: 100px;
+ background: blue;
+ }
+
+ .hit {
+ position: absolute;
+ width: 3px;
+ height: 3px;
+ z-index: 20;
+ background: red;
+ border: 1px solid red;
+ }
+ </style>
+</head>
+<body id="body" style="margin:0; width:100%; height:100%">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var prefs = [
+ ["ui.mouse.radius.enabled", true],
+ ["ui.mouse.radius.inputSource.touchOnly", false],
+ ["ui.mouse.radius.leftmm", 12],
+ ["ui.mouse.radius.topmm", 8],
+ ["ui.mouse.radius.rightmm", 4],
+ ["ui.mouse.radius.bottommm", 4],
+ ["ui.mouse.radius.visitedweight", 50],
+];
+
+var eventTarget;
+var debugHit = [];
+
+function endTest() {
+ SimpleTest.finish();
+ SpecialPowers.removePermission("browser", location.href);
+ for (var pref in prefs) {
+ SpecialPowers.pushPrefEnv({"clear": pref[0]}, function() {});
+ }
+}
+
+function testMouseClick(idPosition, dx, dy, idTarget, msg, options) {
+ eventTarget = null;
+ synthesizeMouse(document.getElementById(idPosition), dx, dy, options || {});
+ try {
+ is(eventTarget.id, idTarget,
+ "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]");
+ } catch (ex) {
+ ok(false, "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]; got " + eventTarget);
+ }
+}
+
+function showDebug() {
+ for (var i = 0; i < debugHit.length; i++) {
+ document.body.appendChild(debugHit[i]);
+ }
+
+ var screenshot = SpecialPowers.snapshotWindow(window, true);
+ dump('IMAGE:' + screenshot.toDataURL() + '\n');
+}
+
+/*
+ Setup the test environment: enabling event fluffing (all ui.* preferences),
+ and enabling remote process.
+*/
+function setupTest(cont) {
+ SpecialPowers.addPermission("browser", true, document);
+ SpecialPowers.pushPrefEnv({"set": prefs}, cont);
+}
+
+function execTest() {
+ /*
+ Creating two iframes that mimics the attention screen behavior on the
+ device:
+ - the 'dialer' iframe is the attention screen you have when a call is
+ in place. it is a green bar, so we copy it as green here too
+ - the 'apps' iframe mimics another application that is being run, be it
+ dialer, sms, ..., anything that the user might want to trigger during
+ a call
+
+ The bug we intent to reproduce here is that in this case, if the user taps
+ onto the top of the 'apps', the event fluffing code will in fact redirect
+ the event to the 'dialer' iframe. In practice, this is bug 921928 where
+ during a call the user wants to place a second call, and while typing the
+ phone number, wants to tap onto the 'delete' key to erase a digit, but ends
+ tapping and activating the dialer.
+ */
+ var dialer = document.createElement('iframe');
+ dialer.id = 'dialer';
+ dialer.src = '';
+ // Force OOP
+ dialer.setAttribute('mozbrowser', 'true');
+ dialer.setAttribute('remote', 'true');
+ document.body.appendChild(dialer);
+
+ var apps = document.createElement('iframe');
+ apps.id = 'apps';
+ apps.src = 'bug921928_event_target_iframe_apps_oop.html';
+ // Force OOP
+ apps.setAttribute('mozbrowser', 'true');
+ apps.setAttribute('remote', 'true');
+ document.body.appendChild(apps);
+
+ var handleEvent = function(event) {
+ eventTarget = event.target;
+
+ // We draw a small red div to show where the event has tapped
+ var hit = document.createElement('div');
+ hit.style.left = (event.clientX - 1.5) + 'px';
+ hit.style.top = (event.clientY - 1.5) + 'px';
+ hit.classList.add('hit');
+ debugHit.push(hit);
+ };
+
+ // In real life, the 'dialer' has a 'mousedown', so we mimic one too,
+ // to reproduce the same behavior
+ dialer.addEventListener('mousedown', function(e) {});
+
+ // This event listener is just here to record what iframe has been hit,
+ // and sets the 'eventTarget' to the iframe's id value so that the
+ // testMouseClick() code can correctly check. We cannot add it on the
+ // 'apps' otherwise it will alter the behavior of the test.
+ document.addEventListener('mousedown', handleEvent);
+
+ // In the following, the coordinates are relative to the iframe
+
+ // First, we check that tapping onto the 'dialer' correctly triggers the
+ // dialer.
+ testMouseClick("dialer", 20, 1, "dialer", "correct hit on dialer with mouse input");
+ testMouseClick("dialer", 20, 1, "dialer", "correct hit on dialer with touch input", {
+ inputSource: MouseEvent.MOZ_SOURCE_TOUCH
+ });
+
+ // Now this is it: we tap inside 'apps', but very close to the border between
+ // 'apps' and 'dialer'. Without the fix from this bug, this test will fail.
+ testMouseClick("apps", 20, 1, "apps", "apps <iframe mozbrowser remote> hit for mouse input");
+ testMouseClick("apps", 20, 1, "apps", "apps <iframe mozbrowser remote> hit for touch input", {
+ inputSource: MouseEvent.MOZ_SOURCE_TOUCH
+ });
+
+ // Show small red spots of where the click happened
+ // showDebug();
+
+ endTest();
+}
+
+function runTest() {
+ setupTest(execTest);
+}
+
+addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_event_target_radius.html b/layout/base/tests/test_event_target_radius.html
new file mode 100644
index 0000000000..caf046cf99
--- /dev/null
+++ b/layout/base/tests/test_event_target_radius.html
@@ -0,0 +1,422 @@
+<!DOCTYPE HTML>
+<html id="html" style="height:100%">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=780847
+-->
+<head>
+ <title>Test radii for mouse events</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .target { position:absolute; left:100px; top:100px; width:100px; height:100px; background:blue; }
+ </style>
+</head>
+<body id="body" onload="setTimeout(startTest, 0)" style="margin:0; width:100%; height:100%; overflow:hidden">
+<p id="display"></p>
+<div id="content">
+ <!-- We make the `t` target shorter than normal in case because we test the
+ bottom edge fluffing on this element, and the test page may be hosted
+ inside a short iframe in the test harness on some platforms.
+ -->
+ <div class="target" style="height:80px" id="t" onmousedown="x=1"></div>
+
+ <div class="target" id="t2" hidden></div>
+
+ <input class="target" id="t3_1" hidden></input>
+ <a href="#" class="target" id="t3_2" hidden></a>
+ <label class="target" id="t3_3" hidden></label>
+ <button class="target" id="t3_4" hidden></button>
+ <select class="target" id="t3_5" hidden></select>
+ <textarea class="target" id="t3_6" hidden></textarea>
+ <div role="button" class="target" id="t3_7" hidden></div>
+ <div role="key" class="target" id="t3_8" hidden></div>
+ <img class="target" id="t3_9" hidden></img>
+
+ <div class="target" style="transform:translate(-80px,0);" id="t4" onmousedown="x=1" hidden></div>
+
+ <div class="target" style="left:0; z-index:1" id="t5_left" onmousedown="x=1" hidden></div>
+ <div class="target" style="left:106px;" id="t5_right" onmousedown="x=1" hidden></div>
+ <div class="target" style="left:0; top:210px;" id="t5_below" onmousedown="x=1" hidden></div>
+
+ <div class="target" id="t6" onmousedown="x=1" style="width: 300px" hidden>
+ <div id="t6_inner" style="position:absolute; left:-40px; top:20px; width:60px; height:60px; background:yellow;"></div>
+ <div id="t6_inner_clickable" style="position:absolute; left:-40px; top: 80px; width: 60px; height: 5px; background:red" onmousedown="x=1"></div>
+ </div>
+ <div id="t6_outer" style="position:absolute; left:360px; top:120px; width:60px; height:60px; background:green;" onmousedown="x=1" hidden></div>
+
+ <div class="target" id="t7" onmousedown="x=1" hidden></div>
+ <div class="target" id="t7_over" hidden></div>
+
+ <div id="t8" contenteditable="true" class="target" hidden></div>
+
+ <div id="t9" class="target" ontouchend="x=1" hidden></div>
+
+ <div id="t10_left" class="target" style="left:-50px;" onmousedown="x=1" hidden></div>
+ <div id="t10_right" class="target" style="left:auto;right:-50px" onmousedown="x=1" hidden></div>
+ <div id="t10_top" class="target" style="top:-50px;" onmousedown="x=1" hidden></div>
+ <div id="t10_bottom" class="target" style="top:auto;bottom:-50px;" onmousedown="x=1" hidden></div>
+ <div id="t10_over" style="position:absolute; left:0; top:0; width:100%; height:100%; background:yellow;" hidden></div>
+
+ <div id="t11" class="target" style="cursor:pointer" hidden></div>
+ <div id="t11_with_child" class="target" style="cursor:pointer" hidden><div id="t11_child" style="width:100px; height:100px; background:green;"></div></div>
+ <div id="t11_covered" class="target" style="cursor:pointer" hidden><div id="t11_coverer" style="width:100px; height:100px; background:green; cursor:text"></div></div>
+
+ <div id="t12" class="target" hidden>
+ <input id="t12_input" style="width: 100px; height: 20px; border: 0; padding: 0"></input>
+ <div id="t12_zisland" style="width: 100px; height: 50px; position:relative; z-index: 5">
+ <div id="t12_target" style="width: 100px; height: 20px; background-color: green"></div>
+ </div>
+ </div>
+
+ <div id="t13" class="target" style="cursor:pointer" hidden>
+ <div id="t13_touchlistener" style="width: 50px; height: 50px; background:red" ontouchend="x=1"></div>
+ <div id="t13_notouchlistener" style="width: 50px; height: 50px; background:green"></div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+function startTest() {
+ SpecialPowers.pushPrefEnv({"set": [["ui.mouse.radius.enabled", true],
+ ["ui.mouse.radius.inputSource.touchOnly", false],
+ ["ui.mouse.radius.leftmm", 12],
+ ["ui.mouse.radius.topmm", 4],
+ ["ui.mouse.radius.rightmm", 4],
+ ["ui.mouse.radius.bottommm", 4],
+ ["ui.mouse.radius.visitedweight", 50]]}, runTest);
+}
+
+
+SimpleTest.waitForExplicitFinish();
+
+let oldResolution = 1.0;
+
+function endTest() {
+ if (navigator.appVersion.includes("Android")) {
+ // Restore any resolution that was changed at the end of the test.
+ let topUtils = SpecialPowers.getDOMWindowUtils(window.top);
+ topUtils.setResolutionAndScaleTo(oldResolution);
+ }
+ SimpleTest.finish();
+}
+
+var eventTarget;
+window.onmousedown = function(event) { eventTarget = event.target; };
+
+function testMouseClick(idPosition, dx, dy, idTarget, msg, options) {
+ eventTarget = null;
+ synthesizeMouse(document.getElementById(idPosition), dx, dy, options || {});
+ try {
+ is(eventTarget.id, idTarget,
+ "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]");
+ } catch (ex) {
+ ok(false, "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]; got " + eventTarget);
+ }
+}
+
+var touchTarget;
+document.addEventListener('touchstart', event => { touchTarget = event.target; });
+
+function testTouch(idPosition, dx, dy, idTarget, msg, options) {
+ touchTarget = null;
+ synthesizeTouch(document.getElementById(idPosition), dx, dy, options || {});
+ try {
+ is(touchTarget.id, idTarget,
+ "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]");
+ } catch (ex) {
+ ok(false, "checking '" + idPosition + "' offset " + dx + "," + dy + " [" + msg + "]; got " + touchTarget);
+ }
+}
+
+function setShowing(id, show) {
+ var e = document.getElementById(id);
+ e.hidden = !show;
+}
+
+var mm;
+function runTest() {
+ let resolution = 1;
+ if (navigator.appVersion.includes("Android")) {
+ // Choose a roundish number for the resolution so that the "midpoint" test below
+ // doesn't mis-target an element due to rounding error.
+ let desiredResolution = 0.75;
+ let topUtils = SpecialPowers.getDOMWindowUtils(window.top);
+ // Save the old resolution for restoration at the end of the test.
+ oldResolution = topUtils.getResolution();
+ topUtils.setResolutionAndScaleTo(desiredResolution);
+ // This test runs on Android, zoomed out. Therefore we need to account
+ // for the resolution as well, because the fluff area is relative to screen
+ // pixels rather than CSS pixels.
+ resolution = topUtils.getResolution();
+ // Make sure we were actually able to zoom out to the desired level.
+ ok(resolution == desiredResolution, "Resolution is " + resolution);
+ }
+ mm = SpecialPowers.getDOMWindowUtils(window.top).physicalMillimeterInCSSPixels / resolution;
+ ok(4*mm >= 10, "WARNING: mm " + mm + " too small in this configuration. Test results will be bogus");
+
+ // Test basic functionality: clicks sufficiently close to the element
+ // should be allowed to hit the element. We test points just inside and
+ // just outside the edges we set up in the prefs.
+ testMouseClick("t", 100 + 13*mm, 10, "body", "basic functionality");
+ testMouseClick("t", 100 + 11*mm, 10, "t", "basic functionality");
+ testMouseClick("t", 10, 80 + 5*mm, "body", "basic functionality");
+ testMouseClick("t", 10, 80 + 3*mm, "t", "basic functionality");
+ testMouseClick("t", -5*mm, 10, "body", "basic functionality");
+ testMouseClick("t", -3*mm, 10, "t", "basic functionality");
+ testMouseClick("t", 10, -5*mm, "body", "basic functionality");
+ testMouseClick("t", 10, -3*mm, "t", "basic functionality");
+
+ // When inputSource.touchOnly is true, mouse input is not retargeted.
+ SpecialPowers.pushPrefEnv({"set": [["ui.mouse.radius.inputSource.touchOnly", true]]}, test2);
+}
+
+function test2() {
+ testMouseClick("t", 100 + 11*mm, 10, "body", "disabled for mouse input");
+ testMouseClick("t", 100 + 11*mm, 10, "t", "enabled for touch input", {
+ inputSource: MouseEvent.MOZ_SOURCE_TOUCH
+ });
+ testMouseClick("t", 100 + 13*mm, 10, "body", "basic functionality for touch", {
+ inputSource: MouseEvent.MOZ_SOURCE_TOUCH
+ });
+ SpecialPowers.pushPrefEnv({"set": [["ui.mouse.radius.inputSource.touchOnly", false]]}, test3);
+}
+
+function test3() {
+ setShowing("t", false);
+
+ // Now test the criteria we use to determine which elements are hittable
+ // this way.
+
+ setShowing("t2", true);
+ var t2 = document.getElementById("t2");
+ // Unadorned DIVs are not click radius targets
+ testMouseClick("t2", 100 + 11*mm, 10, "body", "unadorned DIV");
+ // DIVs with the right event handlers are click radius targets
+ t2.onmousedown = function() {};
+ testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onmousedown");
+ t2.onmousedown = null;
+ testMouseClick("t2", 100 + 11*mm, 10, "body", "DIV with onmousedown removed");
+ t2.onmouseup = function() {};
+ testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onmouseup");
+ t2.onmouseup = null;
+ t2.onclick = function() {};
+ testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onclick");
+ t2.onclick = null;
+ t2.onpointerdown = function() {};
+ testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onpointerdown");
+ t2.onpointerdown = null;
+ testMouseClick("t2", 100 + 11*mm, 10, "body", "DIV with onpointerdown removed");
+ t2.onpointerup = function() {};
+ testMouseClick("t2", 100 + 11*mm, 10, "t2", "DIV with onpointerup");
+ t2.onpointerup = null;
+ testMouseClick("t2", 100 + 11*mm, 10, "body", "DIV with onpointerup removed");
+ // Keypresses don't make click radius targets
+ t2.onkeypress = function() {};
+ testMouseClick("t2", 100 + 11*mm, 10, "body", "DIV with onkeypress");
+ t2.onkeypress = null;
+ setShowing("t2", false);
+
+ // Now check that certain elements are click radius targets and others are not
+ for (var i = 1; i <= 9; ++i) {
+ var id = "t3_" + i;
+ var shouldHit = i <= 8;
+ setShowing(id, true);
+ testMouseClick(id, 100 + 11*mm, 10, shouldHit ? id : "body",
+ "<" + document.getElementById(id).tagName + "> element");
+ setShowing(id, false);
+ }
+
+ // Check that our targeting computations take into account the effects of
+ // CSS transforms
+ setShowing("t4", true);
+ testMouseClick("t4", -1, 10, "t4", "translated DIV");
+ setShowing("t4", false);
+
+ // Test the prioritization of multiple targets based on distance to
+ // the target.
+ setShowing("t5_left", true);
+ setShowing("t5_right", true);
+ setShowing("t5_below", true);
+ testMouseClick("t5_left", 102, 10, "t5_left", "closest DIV is left");
+ testMouseClick("t5_left", 102.5, 10, "t5_left",
+ "closest DIV to midpoint is left because of its higher z-index");
+ testMouseClick("t5_left", 104, 10, "t5_right", "closest DIV is right");
+ testMouseClick("t5_left", 10, 104, "t5_left", "closest DIV is left");
+ testMouseClick("t5_left", 10, 105, "t5_left",
+ "closest DIV to midpoint is left because of its higher z-index");
+ testMouseClick("t5_left", 10, 106, "t5_below", "closest DIV is below");
+ setShowing("t5_left", false);
+ setShowing("t5_right", false);
+ setShowing("t5_below", false);
+
+ // Test behavior of nested elements.
+ // The following behaviors are questionable and may need to be changed.
+ setShowing("t6", true);
+ setShowing("t6_outer", true);
+ testMouseClick("t6_inner", -1, 10, "t6_inner",
+ "inner element is clickable because its parent is, even when it sticks outside parent");
+ testMouseClick("t6_inner", 39, -1, "t6_inner",
+ "when outside both inner and parent, but in range of both, the inner is selected");
+ testMouseClick("t6_inner", 45, -1, "t6_inner",
+ "clicking in clickable parent close to inner activates inner, not parent");
+ testMouseClick("t6_inner_clickable", 1, -1, "t6_inner",
+ "clicking on inner doesn't get redirected to inner_clickable because they are both clickable");
+ testMouseClick("t6_inner_clickable", 1, 1, "t6_inner_clickable",
+ "clicking on inner_clickable doesn't get redirected to inner because they are both clickable");
+ testMouseClick("t6_inner_clickable", 45, -1, "t6_inner",
+ "clicking on inner while backed by its parent still doesn't get redirected to inner_clickable");
+ testMouseClick("t6_inner_clickable", 45, 1, "t6_inner_clickable",
+ "clicking on inner_clickable while backed by its parent still doesn't get redirected to inner");
+ testMouseClick("t6_inner_clickable", 45, 6, "t6_inner_clickable",
+ "clicking on parent near inner_clickable gets redirected to inner_clickable rather than inner because it is closer");
+ // 280 is the distance from t6_inner's right edge to t6's right edge
+ // 240 is the distance from t6_inner's right edge to t6_outer's right edge.
+ // we want to click on t6, but at least 13mm away from t6_inner, so that
+ // t6_inner doesn't steal the click.
+ ok(13*mm < 280, "no point inside t6 that's not within radius of t6_inner; adjust layout of t6/inner/outer as needed");
+ testMouseClick("t6_outer", -240 + 13*mm, -1, "t6",
+ "clicking in clickable container close to outer activates parent, not outer");
+ testMouseClick("t6_outer", 1, 1, "t6_outer",
+ "clicking directly on the outer activates it");
+ setShowing("t6", false);
+ setShowing("t6_outer", false);
+
+ setShowing("t7", true);
+ setShowing("t7_over", true);
+ testMouseClick("t7", 100 + 11*mm, 10, "body", "covered div is not clickable");
+ testMouseClick("t7", 10, 10, "t7_over", "covered div is not clickable even within its bounds");
+ setShowing("t7", false);
+ setShowing("t7_over", false);
+
+ // Check that contenteditable elements are considered clickable for fluffing.
+ setShowing("t8", true);
+ var rect = document.getElementById("t8").getBoundingClientRect();
+ testMouseClick("t8", rect.left + 1, rect.top + 1, "t8", "content editable enabled for mouse input");
+ testMouseClick("t8", rect.left + 1, rect.top + 1, "t8", "content editable enabled for touch input", {
+ inputSource: MouseEvent.MOZ_SOURCE_TOUCH
+ });
+ setShowing("t8", false);
+
+ // Check that elements are touchable
+ setShowing("t9", true);
+ var rect = document.getElementById("t9").getBoundingClientRect();
+ testMouseClick("t9", rect.left + 1, rect.top + 1, "t9", "div enabled with mouse input");
+ testMouseClick("t9", rect.left + 1, rect.top + 1, "t9", "div enabled with touch input", {
+ inputSource: MouseEvent.MOZ_SOURCE_TOUCH
+ });
+ setShowing("t9", false);
+
+ setShowing("t10_over", true);
+ setShowing("t10_left", true);
+ setShowing("t10_right", true);
+ setShowing("t10_top", true);
+ setShowing("t10_bottom", true);
+ testMouseClick("t10_left", 51, 10, "t10_over", "element outside of visible area is not selected");
+ if (self.frameElement &&
+ (self.frameElement.offsetLeft + self.innerWidth >
+ SpecialPowers.wrap(top).innerWidth)) {
+ info("WARNING: Window is too narrow, can't test t10_right");
+ } else {
+ testMouseClick("t10_right", 49, 10, "t10_over", "element outside of visible area is not selected");
+ }
+ testMouseClick("t10_top", 10, 51, "t10_over", "element outside of visible area is not selected");
+ if (self.frameElement &&
+ (self.frameElement.offsetTop + self.innerHeight >
+ SpecialPowers.wrap(top).innerHeight)) {
+ info("WARNING: Window is too short, can't test t10_bottom");
+ } else {
+ testMouseClick("t10_bottom", 10, 49, "t10_over", "element outside of visible area is not selected");
+ }
+ setShowing("t10_over", false);
+ setShowing("t10_left", false);
+ setShowing("t10_right", false);
+ setShowing("t10_top", false);
+ setShowing("t10_bottom", false);
+
+ setShowing("t11", true);
+ testMouseClick("t11", 100 + 11*mm, 10, "t11",
+ "Elements with cursor:pointer are fluff targets");
+ setShowing("t11", false);
+
+ setShowing("t11_with_child", true);
+ testMouseClick("t11_with_child", 100 + 11*mm, 10, "t11_child",
+ "Elements that inherit cursor:pointer are fluff targets");
+ setShowing("t11_with_child", false);
+
+ setShowing("t11_covered", true);
+ testMouseClick("t11_covered", 100 + 11*mm, 10, "body",
+ "Elements that override an inherited cursor:pointer are not fluff targets");
+ setShowing("t11_covered", false);
+
+ setShowing("t12", true);
+ testMouseClick("t12_target", 1, 1, "t12_target",
+ "Event retargeting should not escape out from a z-index ancestor");
+ setShowing("t12", false);
+
+ // Click empty area of textarea
+ let textarea = document.getElementById("t3_6");
+ textarea.value = "foo bar baz\nfoo"
+ textarea.style.height = "3.3em";
+ textarea.style.lineHeight = "1.1";
+ setShowing("t3_6", true);
+ textarea.selectionStart = 0;
+ synthesizeMouseAtCenter(textarea, {});
+ is(textarea.selectionStart, textarea.value.length,
+ "selection should be set to last character");
+
+ textarea.value = ""
+ textarea.style.height = "auto";
+ textarea.style.lineHeight = "";
+ setShowing("t3_6", false);
+
+ // Not yet tested:
+ // -- visited link weight
+ // -- "Closest" using Euclidean distance
+
+
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", 1],
+ ["ui.touch.radius.enabled", true],
+ ["ui.touch.radius.leftmm", 12],
+ ["ui.touch.radius.topmm", 4],
+ ["ui.touch.radius.rightmm", 4],
+ ["ui.touch.radius.bottommm", 4],
+ ["ui.touch.radius.visitedweight", 50]]}, testTouchable);
+}
+
+function testTouchable() {
+ // Element "t" has a mousedown listener but no touch listener. So the touches
+ // that land immediately outside "t" should not hit "t"; only the touches
+ // directly aimed at "t" should hit it.
+ setShowing("t", true);
+ var rect = document.getElementById("t").getBoundingClientRect();
+ testTouch("t", rect.width - 1, 10, "t", "touch inside t right edge");
+ testTouch("t", rect.width + 1, 10, "body", "touch outside t right edge");
+ testTouch("t", 10, rect.height - 1, "t", "touch inside t bottom edge");
+ testTouch("t", 10, rect.height + 1, "body", "touch outside t bottom edge");
+ testTouch("t", 1, 10, "t", "touch inside t left edge");
+ testTouch("t", -1, 10, "body", "touch outside t left edge");
+ testTouch("t", 10, 1, "t", "touch inside t top edge");
+ testTouch("t", 10, -1, "body", "touch outside t top edge");
+ setShowing("t", false);
+
+ // Element "t9" has a touchend listener, so touches within the radius
+ // distance from it should hit it.
+ setShowing("t9", true);
+ testTouch("t9", -5*mm, 10, "body", "touch outside t9 left edge radius");
+ testTouch("t9", -3*mm, 10, "t9", "touch inside t9 left edge radius");
+ setShowing("t9", false);
+
+ // Element "t13" is clickable, so touches on descendants should not get retargeted.
+ // In particular, the touch that lands on t13_notouchlistener but within the touch radius
+ // of t13_touchlistener should not get retargeted.
+ setShowing("t13", true);
+ testTouch("t13", 10, 50 + (2*mm), "t13_notouchlistener", "touch outside t13_touchlistener bottom edge");
+ testTouch("t13", 10, 50 - (2*mm), "t13_touchlistener", "touch inside t13_touchlistener bottom edge");
+ setShowing("t13", false);
+
+ endTest();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_frame_reconstruction_body_table.html b/layout/base/tests/test_frame_reconstruction_body_table.html
new file mode 100644
index 0000000000..b1f6af0481
--- /dev/null
+++ b/layout/base/tests/test_frame_reconstruction_body_table.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>
+ Test for Bug 1630819: Test we don't reframe the html element when
+ inserting a block element into a display:table body element.
+ </title>
+ <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ const utils = SpecialPowers.getDOMWindowUtils(window);
+
+ function runTest() {
+ document.documentElement.offsetTop;
+
+ const frameCountBeforeReframe = utils.framesConstructed;
+
+ // We expect to construct one newly appended block, and reconstruct the
+ // display:table <body>, which consists of 8 frames including TableWrapper,
+ // Table, TableRowGroup, TableRow, TableColGroup, TableCol, TableCell, and
+ // TableCell's inner block.
+ const expectedFrameConstructionCount = 1 + 8;
+
+ let div = document.createElement("div");
+ document.body.appendChild(div);
+ document.documentElement.offsetTop;
+
+ is(utils.framesConstructed - frameCountBeforeReframe,
+ expectedFrameConstructionCount,
+ "We shouldn't reframe <html> when appending a <div> into a display:table <body>!");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <style>
+ body {
+ display: table;
+ }
+ </style>
+
+ <body onload="runTest();"></body>
+</html>
diff --git a/layout/base/tests/test_frame_reconstruction_body_writing_mode.html b/layout/base/tests/test_frame_reconstruction_body_writing_mode.html
new file mode 100644
index 0000000000..70c7e3a0f0
--- /dev/null
+++ b/layout/base/tests/test_frame_reconstruction_body_writing_mode.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>
+ Test for Bug 1593752: Test we don't reframe the html element when
+ inserting a canonical body element with the same writing-mode.
+ </title>
+ <link rel="author" title="Ting-Yu Lin" href="tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ const utils = SpecialPowers.getDOMWindowUtils(window);
+
+ function runTest() {
+ document.documentElement.offsetTop;
+
+ // We expect to construct only the canonical body because its writing-mode
+ // is the same as html's writing-mode.
+ const expectedFrameConstructCount = utils.framesConstructed + 1;
+ document.body.before(document.createElement("body"));
+ document.documentElement.offsetTop;
+
+ is(utils.framesConstructed, expectedFrameConstructCount,
+ "We should not reframe <html>!");
+
+ SimpleTest.finish();
+ }
+ </script>
+ <body onload="runTest();"></body>
+</html>
diff --git a/layout/base/tests/test_frame_reconstruction_for_column_span.html b/layout/base/tests/test_frame_reconstruction_for_column_span.html
new file mode 100644
index 0000000000..c368901241
--- /dev/null
+++ b/layout/base/tests/test_frame_reconstruction_for_column_span.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>
+ Test for Bug 1503420: Test we don't reframe multi-column containing block
+ when appending a block containing a spanner kid.
+ </title>
+ <link rel="author" title="Ting-Yu Lin" href="mailto:tlin@mozilla.com">
+ <link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ const utils = SpecialPowers.getDOMWindowUtils(window);
+
+ function appendBlock() {
+ // Create a subtree like the following, and append it to columns.
+ // <div>
+ // <h3>spanner</h3>
+ // block2
+ // </div>
+ var spanner = document.createElement("h3");
+ var spannerText = document.createTextNode("spanner");
+ spanner.appendChild(spannerText);
+
+ var block2 = document.createElement("div");
+ var block2Text = document.createTextNode("block2");
+ block2.appendChild(spanner);
+ block2.appendChild(block2Text)
+
+ var column = document.getElementById("column");
+ column.appendChild(block2);
+ }
+
+ function runTest() {
+ document.documentElement.offsetTop;
+ // We expected to construct 6 more frames.
+ // 1) Block frame for <div>
+ // 2) Block frame for <h3>
+ // 3) Text frame for "spanner"
+ // 4) Text frame for "block2"
+ // 5) Column-span wrapper for <h3>, which is a sibling of <div>
+ // 6) Column-span wrapper for 5), which is a sibling of <article>
+ // Note: creating a continuation frame doesn't increase the count.
+ const expectedFrameConstructCount = utils.framesConstructed + 6;
+
+ appendBlock();
+ document.documentElement.offsetTop;
+
+ is(utils.framesConstructed, expectedFrameConstructCount,
+ "We shouldn't construct unexpected frames.");
+
+ SimpleTest.finish();
+ }
+ </script>
+
+ <style>
+ #column {
+ column-count: 3;
+ column-rule: 6px solid;
+ width: 400px;
+ outline: 1px solid black;
+ }
+ h3 {
+ column-span: all;
+ outline: 1px solid blue;
+ }
+ </style>
+
+ <body onload="runTest();">
+ <article id="column">
+ <div>block1</div>
+ </article>
+ </body>
+</html>
diff --git a/layout/base/tests/test_frame_reconstruction_for_pseudo_elements.html b/layout/base/tests/test_frame_reconstruction_for_pseudo_elements.html
new file mode 100644
index 0000000000..f76e9f139f
--- /dev/null
+++ b/layout/base/tests/test_frame_reconstruction_for_pseudo_elements.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1110277
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1110277</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .testspan {
+ color: yellow;
+ }
+ .testspan[attributestate],
+ .testspan[attributestate]::before, .testspan[attributestate]::after {
+ color: blue;
+ }
+
+ #firstlinetest::first-line {
+ color: purple;
+ }
+ #firstlinetest > .testspan::before {
+ content: "[*]";
+ }
+
+ #aftertest > .testspan::after {
+ content: "[*]";
+ }
+ </style>
+ <script type="application/javascript">
+
+ /** Test for Bug 1110277 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function run() {
+ runtest("first line test", "#firstlinetest > .testspan");
+ runtest("after test", "#aftertest > .testspan");
+ SimpleTest.finish();
+ }
+
+ function runtest(description, selector) {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ var span = document.querySelector(selector);
+ var cs = getComputedStyle(span, "");
+
+ var startcolor = cs.color;
+ var startcount = utils.framesConstructed;
+ is(startcolor, "rgb(255, 255, 0)", description + ": initial color");
+
+ span.setAttribute("attributestate", "true");
+
+ var endcolor = cs.color;
+ var endcount = utils.framesConstructed;
+ is(endcolor, "rgb(0, 0, 255)", description + ": final color");
+ is(endcount, startcount,
+ description + ": should not do frame construction")
+ }
+
+ </script>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1110277">Mozilla Bug 1110277</a>
+<div id="firstlinetest">
+ <span class="testspan">This <span style="display:block">is a</span> test.</span>
+</div>
+<div id="aftertest">
+ <span class="testspan">This <span style="display:block">is a</span> test.</span>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_frame_reconstruction_for_svg_transforms.html b/layout/base/tests/test_frame_reconstruction_for_svg_transforms.html
new file mode 100644
index 0000000000..ac0b5d8191
--- /dev/null
+++ b/layout/base/tests/test_frame_reconstruction_for_svg_transforms.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1419764
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1419764</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1419764 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ function run() {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ var rect = document.querySelector("rect");
+
+ var matrix = rect.transform.baseVal[0].matrix;
+
+ matrix.e = 100;
+ document.documentElement.offsetTop; // flush layout
+
+ var startcount = utils.framesConstructed;
+
+ matrix.e = 200;
+ document.documentElement.offsetTop; // flush layout
+
+ var endcount = utils.framesConstructed;
+ is(endcount, startcount, "should not do frame construction");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1419764">Mozilla Bug 1419764</a>
+<svg>
+ <rect transform="translate(1 1)" width="20" height="20" fill="yellow"/>
+</svg>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_frame_reconstruction_scroll_restore.html b/layout/base/tests/test_frame_reconstruction_scroll_restore.html
new file mode 100644
index 0000000000..a5115bb694
--- /dev/null
+++ b/layout/base/tests/test_frame_reconstruction_scroll_restore.html
@@ -0,0 +1,82 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1268195
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1268195</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ html, body {
+ margin: 0;
+ padding: 0;
+ }
+
+ .noscroll {
+ overflow: hidden;
+ height: 100%;
+ }
+
+ /* Toggling this on and off triggers a frame reconstruction on the <body> */
+ html.reconstruct-body::before {
+ top: 0;
+ content: '';
+ display: block;
+ height: 2px;
+ position: absolute;
+ width: 100%;
+ z-index: 99;
+ }
+ </style>
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ function run() {
+ // Make sure we have the right scroll element
+ SimpleTest.is(document.body.scrollTopMax > 0, true, "Body is the scrolling element");
+
+ // Scroll to the bottom
+ document.body.scrollTop = document.body.scrollTopMax;
+ SimpleTest.is(document.body.scrollTop > 0, true, "Scrolled body");
+
+ // Do a frame reconstruction on the body while also shortening the
+ // height, but still keep it long enough to be scrollable.
+ document.body.classList.toggle('noscroll');
+ document.documentElement.classList.toggle('reconstruct-body');
+ document.getElementById('spacer').style.height = '1000px';
+ var reducedMax = document.body.scrollTopMax;
+ SimpleTest.is(document.body.scrollTop, reducedMax, `Scroll forced to new bottom ${reducedMax}`);
+
+ // Do another frame reconstruction while lengthening the height again.
+ document.body.classList.toggle('noscroll');
+ document.documentElement.classList.toggle('reconstruct-body');
+ document.getElementById('spacer').style.height = '5000px';
+ SimpleTest.is(document.body.scrollTop, reducedMax, "Scroll remained at reduced height");
+
+ // Do a frame reconstruction on the body while also shortening the
+ // height, this time down to a non-scrollable height.
+ document.body.classList.toggle('noscroll');
+ document.documentElement.classList.toggle('reconstruct-body');
+ document.getElementById('spacer').style.height = '1px';
+ SimpleTest.is(document.body.scrollTop, 0, "Scroll forced to top");
+
+ // Do another frame reconstruction while lengthening the height again.
+ document.body.classList.toggle('noscroll');
+ document.documentElement.classList.toggle('reconstruct-body');
+ document.getElementById('spacer').style.height = '5000px';
+ SimpleTest.is(document.body.scrollTop, 0, "Scroll remained at top");
+
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="setTimeout(run, 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1268195">Mozilla Bug 1268195</a><br/>
+The scroll position should end the top of the page. This is the top, yay!
+<div id="spacer" style="height: 5000px"></div>
+The scroll position should end the top of the page. This is the bottom!
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html b/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html
new file mode 100644
index 0000000000..3b0431f458
--- /dev/null
+++ b/layout/base/tests/test_getBoxQuads_convertPointRectQuad.html
@@ -0,0 +1,717 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="startTest()">
+<p id="display"></p>
+<script>
+// Global variables we want eval() to be able to reference from anywhere
+var f1d;
+var text;
+var suppressedText;
+var suppressedText2;
+var comment;
+var fragment;
+var openedWindow;
+var zeroPoint;
+var zeroRect;
+var zeroQuad;
+var notInDocument = document.createElement('div');
+
+function isEval(expr, b) {
+ // we ignore an insignificant discrepancy in floating-point values
+ var exprVal = eval(expr);
+ if (exprVal != b && Math.abs(exprVal - b) < 0.0001) {
+ ok(true, expr + " (" + exprVal + " within 0.0001 of " + b + ")");
+ return;
+ }
+ is(exprVal, b, expr);
+}
+
+function isApprox(a, b, msg, options) {
+ if (a != b && 'tolerance' in options &&
+ Math.abs(a - b) < options.tolerance) {
+ ok(true, msg + "(" + a + " within " + options.tolerance + " of " + b + ")");
+ return;
+ }
+ is(a, b, msg);
+}
+
+function makeQuadsExpr(fromStr, options) {
+ var getBoxQuadsOptionParts = [];
+ if ('box' in options) {
+ getBoxQuadsOptionParts.push("box:'" + options.box + "'");
+ }
+ if ('toStr' in options) {
+ getBoxQuadsOptionParts.push("relativeTo:" + options.toStr);
+ }
+ return fromStr + ".getBoxQuads({" + getBoxQuadsOptionParts.join(',') + "})";
+}
+
+function makePointExpr(fromStr, options, x, y) {
+ var convertPointOptionParts = [];
+ if ('box' in options) {
+ convertPointOptionParts.push("fromBox:'" + options.box + "'");
+ }
+ if ('toBox' in options) {
+ convertPointOptionParts.push("toBox:'" + options.toBox + "'");
+ }
+ return ('toStr' in options ? options.toStr : "document") +
+ ".convertPointFromNode(new DOMPoint(" + x + "," + y + ")," + fromStr + ",{" +
+ convertPointOptionParts.join(",") + "})";
+}
+
+function checkConvertPoints(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4) {
+ var selfQuads = eval(fromStr).getBoxQuads(
+ {box:options.box == "" ? "border" : options.box,
+ relativeTo:eval(fromStr)});
+ var boxWidth = selfQuads[0].getBounds().width;
+ var boxHeight = selfQuads[0].getBounds().height;
+
+ var convertTopLeftPointExpr = makePointExpr(fromStr, options, 0, 0);
+ var topLeft = eval(convertTopLeftPointExpr);
+ isApprox(topLeft.x, x1, convertTopLeftPointExpr + ".x", options);
+ isApprox(topLeft.y, y1, convertTopLeftPointExpr + ".y", options);
+
+ var convertTopRightPointExpr = makePointExpr(fromStr, options, boxWidth, 0);
+ var topRight = eval(convertTopRightPointExpr);
+ isApprox(topRight.x, x2, convertTopRightPointExpr + ".x", options);
+ isApprox(topRight.y, y2, convertTopRightPointExpr + ".y", options);
+
+ var convertBottomRightPointExpr = makePointExpr(fromStr, options, boxWidth, boxHeight);
+ var bottomRight = eval(convertBottomRightPointExpr);
+ isApprox(bottomRight.x, x3, convertBottomRightPointExpr + ".x", options);
+ isApprox(bottomRight.y, y3, convertBottomRightPointExpr + ".y", options);
+
+ var convertBottomLeftPointExpr = makePointExpr(fromStr, options, 0, boxHeight);
+ var bottomLeft = eval(convertBottomLeftPointExpr);
+ isApprox(bottomLeft.x, x4, convertBottomLeftPointExpr + ".x", options);
+ isApprox(bottomLeft.y, y4, convertBottomLeftPointExpr + ".y", options);
+}
+
+function checkConvertRect(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4) {
+ var selfQuads = eval(fromStr).getBoxQuads(
+ {box:options.box == "" ? "border" : options.box,
+ relativeTo:eval(fromStr)});
+ var boxWidth = selfQuads[0].getBounds().width;
+ var boxHeight = selfQuads[0].getBounds().height;
+
+ var convertPointOptionParts = [];
+ if ('box' in options) {
+ convertPointOptionParts.push("fromBox:'" + options.box + "'");
+ }
+ if ('toBox' in options) {
+ convertPointOptionParts.push("toBox:'" + options.toBox + "'");
+ }
+
+ var convertRectExpr = ('toStr' in options ? options.toStr : "document") +
+ ".convertRectFromNode(new DOMRect(0,0," + boxWidth + "," + boxHeight + ")," +
+ fromStr + ",{" + convertPointOptionParts.join(",") + "})";
+ var quad = eval(convertRectExpr);
+ isApprox(quad.p1.x, x1, convertRectExpr + ".p1.x", options);
+ isApprox(quad.p1.y, y1, convertRectExpr + ".p1.y", options);
+ isApprox(quad.p2.x, x2, convertRectExpr + ".p2.x", options);
+ isApprox(quad.p2.y, y2, convertRectExpr + ".p2.y", options);
+ isApprox(quad.p3.x, x3, convertRectExpr + ".p3.x", options);
+ isApprox(quad.p3.y, y3, convertRectExpr + ".p3.y", options);
+ isApprox(quad.p4.x, x4, convertRectExpr + ".p4.x", options);
+ isApprox(quad.p4.y, y4, convertRectExpr + ".p4.y", options);
+}
+
+function checkConvertQuad(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4) {
+ var selfQuads = eval(fromStr).getBoxQuads(
+ {box:options.box == "" ? "border" : options.box,
+ relativeTo:eval(fromStr)});
+ var boxWidth = selfQuads[0].getBounds().width;
+ var boxHeight = selfQuads[0].getBounds().height;
+
+ var convertPointOptionParts = [];
+ if ('box' in options) {
+ convertPointOptionParts.push("fromBox:'" + options.box + "'");
+ }
+ if ('toBox' in options) {
+ convertPointOptionParts.push("toBox:'" + options.toBox + "'");
+ }
+
+ var convertQuadExpr = ('toStr' in options ? options.toStr : "document") +
+ ".convertQuadFromNode(new DOMQuad(new DOMRect(0,0," + boxWidth + "," + boxHeight + "))," +
+ fromStr + ",{" + convertPointOptionParts.join(",") + "})";
+ var quad = eval(convertQuadExpr);
+ isApprox(quad.p1.x, x1, convertQuadExpr + ".p1.x", options);
+ isApprox(quad.p1.y, y1, convertQuadExpr + ".p1.y", options);
+ isApprox(quad.p2.x, x2, convertQuadExpr + ".p2.x", options);
+ isApprox(quad.p2.y, y2, convertQuadExpr + ".p2.y", options);
+ isApprox(quad.p3.x, x3, convertQuadExpr + ".p3.x", options);
+ isApprox(quad.p3.y, y3, convertQuadExpr + ".p3.y", options);
+ isApprox(quad.p4.x, x4, convertQuadExpr + ".p4.x", options);
+ isApprox(quad.p4.y, y4, convertQuadExpr + ".p4.y", options);
+}
+
+function checkQuadIsRect(fromStr, options, x, y, w, h) {
+ var quadsExpr = makeQuadsExpr(fromStr, options);
+ var quads = eval(quadsExpr);
+ is(quads.length, 1, quadsExpr + " checking quad count");
+ var q = quads[0];
+ isApprox(q.p1.x, x, quadsExpr + " checking quad.p1.x", options);
+ isApprox(q.p1.y, y, quadsExpr + " checking quad.p1.y", options);
+ isApprox(q.p2.x, x + w, quadsExpr + " checking quad.p2.x", options);
+ isApprox(q.p2.y, y, quadsExpr + " checking quad.p2.y", options);
+ isApprox(q.p3.x, x + w, quadsExpr + " checking quad.p3.x", options);
+ isApprox(q.p3.y, y + h, quadsExpr + " checking quad.p3.y", options);
+ isApprox(q.p4.x, x, quadsExpr + " checking quad.p4.x", options);
+ isApprox(q.p4.y, y + h, quadsExpr + " checking quad.p4.y", options);
+
+ isApprox(q.getBounds().left, x, quadsExpr + " checking quad.getBounds().left", options);
+ isApprox(q.getBounds().top, y, quadsExpr + " checking quad.getBounds().top", options);
+ isApprox(q.getBounds().width, w, quadsExpr + " checking quad.getBounds().width", options);
+ isApprox(q.getBounds().height, h, quadsExpr + " checking quad.getBounds().height", options);
+
+ checkConvertPoints(fromStr, options, x, y, x + w, y, x + w, y + h, x, y + h);
+ checkConvertRect(fromStr, options, x, y, x + w, y, x + w, y + h, x, y + h);
+ checkConvertQuad(fromStr, options, x, y, x + w, y, x + w, y + h, x, y + h);
+}
+
+function checkQuadIsQuad(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4) {
+ var quadsExpr = makeQuadsExpr(fromStr, options);
+ var quads = eval(quadsExpr);
+ is(quads.length, 1, quadsExpr + " checking quad count");
+ var q = quads[0];
+ isApprox(q.p1.x, x1, quadsExpr + " checking quad.p1.x", options);
+ isApprox(q.p1.y, y1, quadsExpr + " checking quad.p1.y", options);
+ isApprox(q.p2.x, x2, quadsExpr + " checking quad.p2.x", options);
+ isApprox(q.p2.y, y2, quadsExpr + " checking quad.p2.y", options);
+ isApprox(q.p3.x, x3, quadsExpr + " checking quad.p3.x", options);
+ isApprox(q.p3.y, y3, quadsExpr + " checking quad.p3.y", options);
+ isApprox(q.p4.x, x4, quadsExpr + " checking quad.p4.x", options);
+ isApprox(q.p4.y, y4, quadsExpr + " checking quad.p4.y", options);
+
+ isApprox(q.getBounds().left, Math.min(x1,x2,x3,x4), quadsExpr + " checking quad.getBounds().left", options);
+ isApprox(q.getBounds().top, Math.min(y1,y2,y3,y4), quadsExpr + " checking quad.getBounds().top", options);
+ isApprox(q.getBounds().right, Math.max(x1,x2,x3,x4), quadsExpr + " checking quad.getBounds().right", options);
+ isApprox(q.getBounds().bottom, Math.max(y1,y2,y3,y4), quadsExpr + " checking quad.getBounds().bottom", options);
+
+ checkConvertPoints(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4);
+ checkConvertRect(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4);
+ checkConvertQuad(fromStr, options, x1, y1, x2, y2, x3, y3, x4, y4);
+}
+
+function checkException(expr, name) {
+ try {
+ eval(expr);
+ ok(false, "Exception should have been thrown for " + expr);
+ } catch (ex) {
+ is(ex.name, name, "Checking exception type for " + expr);
+ }
+}
+
+function checkNotFound(fromStr, toStr, x1, y1, x2, y2) {
+ var convertPointExpr = toStr + ".convertPointFromNode(new DOMPoint(" + x1 +
+ "," + y1 + ")," + fromStr + ")";
+ checkException(convertPointExpr, "NotFoundError");
+
+ var convertRectExpr = toStr + ".convertRectFromNode(new DOMRect(" + x1 +
+ "," + y1 + "," + x2 + "," + y2 + ")," + fromStr + ")";
+ checkException(convertRectExpr, "NotFoundError");
+
+ var convertQuadExpr = toStr + ".convertQuadFromNode(new DOMQuad(new DOMRect(" + x1 +
+ "," + y1 + "," + x2 + "," + y2 + "))," + fromStr + ")";
+ checkException(convertQuadExpr, "NotFoundError");
+}
+</script>
+<style>
+em {
+ display:inline-block; height:10px; background:gray;
+}
+</style>
+<div id="dContainer"
+ style="padding:13px 14px 15px 16px;
+ border-width:17px 18px 19px 20px; border-style:solid; border-color:yellow;
+ margin:21px 22px 23px 24px;">
+ <div id="d"
+ style="width:120px; height:90px; padding:1px 2px 3px 4px;
+ border-width:5px 6px 7px 8px; border-style:solid; border-color:yellow;
+ margin:9px 10px 11px 12px; background:blue;">
+ </div>
+</div>
+
+<div id="dUnrelated" style="width:50px; height:50px;"></div>
+
+<iframe id="f1" style="width:50px; height:50px; border:0; background:lime;"
+ src="file_getBoxQuads_convertPointRectQuad_frame1.html">
+</iframe>
+<!--
+It matters that the first part of this span is on the same line as the above <iframe>!
+That ensures the first quad's X position is not equal to the anonymous block's X position.
+-->
+<span id="ibSplit"
+ ><em id="ibSplitPart1" style="width:100px;"></em
+ ><div style="width:110px; height:20px; background:black"></div
+ ><em style="width:130px;"></em></span>
+
+<table cellspacing="0" id="table" style="border:0; margin:8px; padding:0; background:orange">
+ <tbody style="padding:0; margin:0; border:0; background:blue">
+ <tr style="height:50px; padding:0; margin:0; border:0">
+ <td style="border:0; margin:0; padding:0">Cell</td>
+ </tr>
+ </tbody>
+ <caption style="height:40px; background:yellow">Caption</caption>
+</table>
+
+<div style="height:80px; column-count:2; column-fill:auto; border:2px solid black;">
+ <div style="height:20px;"></div>
+ <div id="colSplit" style="height:80px; background:blue; border:10px solid red; border-bottom-width:15px"></div>
+</div>
+
+<div style="width:200px; border:2px solid black;"
+ ><em style="width:150px;"></em
+ ><span id="inlineSplit" style="background:pink; border:10px solid red; border-right-width:15px"
+ ><em style="width:20px; background:green"></em><em style="width:60px"></em
+ ></span
+></div>
+
+<div style="width:200px; border:2px solid black;"
+ ><em style="width:150px;"></em
+ ><span id="textContainer">T
+TextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextTextText</span
+></div>
+
+<div id="suppressedTextContainer"> </div>
+<div id="suppressedTextContainer2"> </div>
+
+<div id="commentContainer"><!-- COMMENT --></div>
+
+<div id="displayNone" style="display:none"></div>
+
+<div id="overflowHidden"
+ style="overflow:hidden; width:120px; height:90px; padding:1px 2px 3px 4px;
+ border-width:5px 6px 7px 8px; border-style:solid; border-color:yellow;
+ margin:9px 10px 11px 12px; background:blue;">
+ <div style="height:400px; background:lime;"></div>
+</div>
+
+<div id="overflowScroll"
+ style="overflow:scroll; width:120px; height:90px; padding:1px 2px 3px 4px;
+ border-width:5px 6px 7px 8px; border-style:solid; border-color:yellow;
+ margin:9px 10px 11px 12px; background:blue; background-clip:content-box;">
+ <div id="overflowScrollChild" style="height:400px;"></div>
+</div>
+
+<div id="scaleTransformContainer" style="width:200px; height:200px;">
+ <div id="scaleTransform"
+ style="transform:scale(2); transform-origin:top left; width:70px; height:80px; background:yellow"></div>
+</div>
+
+<div id="translateTransformContainer" style="width:200px; height:200px;">
+ <div id="translateTransform"
+ style="transform:translate(30px,40px); width:70px; height:80px; background:yellow"></div>
+</div>
+
+<div id="rotateTransformContainer" style="width:200px; height:200px;">
+ <div id="rotateTransform"
+ style="transform:rotate(90deg); width:70px; height:80px; background:yellow"></div>
+</div>
+
+<div id="flipTransformContainer" style="width:200px; height:200px;">
+ <div id="flipTransform"
+ style="transform:scaleY(-1); width:70px; height:80px; background:yellow"></div>
+</div>
+
+<div id="rot45TransformContainer" style="width:200px; height:200px;">
+ <div id="rot45Transform"
+ style="transform:rotate(45deg); width:100px; height:100px; background:yellow"></div>
+</div>
+
+<div id="singularTransform" style="transform:scale(0); width:200px; height:200px;">
+ <div id="singularTransformChild1" style="height:50px;"></div>
+ <div id="singularTransformChild2" style="height:50px;"></div>
+</div>
+
+<div id="threeDTransformContainer" style="perspective:600px; width:200px; height:200px">
+ <div id="threeDTransform" style="transform:rotateY(70deg); background:yellow; height:100px; perspective:600px">
+ <div id="threeDTransformChild" style="transform:rotateY(-70deg); background:blue; height:50px;"></div>
+ </div>
+</div>
+
+<div id="preserve3DTransformContainer" style="perspective:600px; width:200px; height:200px">
+ <div id="preserve3DTransform" style="transform:rotateY(70deg); transform-style:preserve-3d; background:yellow; height:100px;">
+ <div id="preserve3DTransformChild" style="transform:rotateY(-70deg); background:blue; height:50px;"></div>
+ </div>
+</div>
+
+<div id="svgContainer">
+ <svg id="svg" style="width:200px; height:200px; background:lightgray; border:7px solid blue; padding:4px">
+ <circle id="circle" cx="50" cy="50" r="20" fill="red" style="margin:20px; padding:10px; border:15px solid black"></circle>
+ <g transform="scale(2)">
+ <foreignObject x="50" y="20">
+ <div id="foreign" style="width:100px; height:60px; background:purple"></div>
+ </foreignObject>
+ </g>
+ </svg>
+</div>
+
+<script>
+SimpleTest.waitForExplicitFinish();
+
+window.scrollTo(0,0);
+
+function startTest() {
+ SpecialPowers.pushPrefEnv({"set": [["layout.css.getBoxQuads.enabled", true],
+ ["layout.css.convertFromNode.enabled", true]]}, grabFeatures);
+}
+
+// This is a bit of a hack but it works. Our Window object was set up while
+// the prefs might have been false so it may not have the features we're
+// testing. Create an <iframe> whose Window is initialized while the prefs are
+// true, and we can steal the features from that Window.
+// When these prefs are enabled on all builds by default, we can skip this step.
+function grabFeatures() {
+ var x = document.createElement('iframe');
+ x.src = "about:blank";
+ document.body.appendChild(x);
+ function setupFeatures(w) {
+ for (var name of ["getBoxQuads", "convertQuadFromNode", "convertRectFromNode", "convertPointFromNode"]) {
+ w.Text.prototype[name] = x.contentWindow.Text.prototype[name];
+ w.Element.prototype[name] = x.contentWindow.Element.prototype[name];
+ w.Document.prototype[name] = x.contentWindow.Document.prototype[name];
+ }
+ for (var name of ["DOMPoint", "DOMQuad"]) {
+ w[name] = x.contentWindow[name];
+ }
+ }
+ x.onload = function() {
+ setupFeatures(window);
+ setupFeatures(f1.contentWindow);
+ runTest();
+ };
+}
+
+function runTest() {
+ zeroPoint = new DOMPoint(0,0);
+ zeroRect = new DOMRect(0,0,0,0);
+ zeroQuad = new DOMQuad(zeroRect);
+
+ // Setup globals
+ f1d = f1.contentWindow.f1d;
+ text = textContainer.firstChild;
+ suppressedText = suppressedTextContainer.firstChild;
+ suppressedText2 = suppressedTextContainer2.firstChild;
+ comment = commentContainer.firstChild;
+ fragment = document.createDocumentFragment();
+
+ // Test basic BoxQuadOptions.box.
+ var dX = d.getBoundingClientRect().left;
+ var dY = d.getBoundingClientRect().top;
+ var dW = d.getBoundingClientRect().width;
+ var dH = d.getBoundingClientRect().height;
+
+ checkQuadIsRect("d", {box:"content"},
+ dX + 4 + 8, dY + 1 + 5, 120, 90);
+ checkQuadIsRect("d", {box:"padding"},
+ dX + 8, dY + 5, 120 + 2 + 4, 90 + 1 + 3);
+ checkQuadIsRect("d", {box:"border"},
+ dX, dY, dW, dH);
+ checkQuadIsRect("d", {},
+ dX, dY, 120 + 2 + 4 + 6 + 8, 90 + 1 + 3 + 5 + 7);
+ checkQuadIsRect("d", {box:"margin"},
+ dX - 12, dY - 9, 120 + 2 + 4 + 6 + 8 + 10 + 12, 90 + 1 + 3 + 5 + 7 + 9 + 11);
+
+ // Test basic BoxQuadOptions.relativeTo
+ checkQuadIsRect("d", {toStr:"dContainer"},
+ 12 + 16 + 20, 9 + 13 + 17, dW, dH);
+
+ // Test BoxQuadOptions.relativeTo relative to this document
+ checkQuadIsRect("d", {toStr:"document"},
+ dX, dY, dW, dH);
+ // Test BoxQuadOptions.relativeTo relative to a non-ancestor.
+ var dUnrelatedX = dUnrelated.getBoundingClientRect().left;
+ var dUnrelatedY = dUnrelated.getBoundingClientRect().top;
+ checkQuadIsRect("d", {toStr:"dUnrelated"},
+ dX - dUnrelatedX, dY - dUnrelatedY, dW, dH);
+ // Test BoxQuadOptions.relativeTo relative to an element in a different document (and the document)
+ var f1X = f1.getBoundingClientRect().left;
+ var f1Y = f1.getBoundingClientRect().top;
+ checkQuadIsRect("d", {toStr:"f1.contentWindow.f1d"},
+ dX - (f1X + 14), dY - (f1Y + 15), dW, dH);
+ checkQuadIsRect("d", {toStr:"f1.contentDocument"},
+ dX - f1X, dY - f1Y, dW, dH);
+ // Test one document relative to another
+ checkQuadIsRect("f1.contentDocument", {toStr:"document"},
+ f1X, f1Y, 50, 50);
+ // The box type is irrelevant for a document
+ checkQuadIsRect("f1.contentDocument", {toStr:"document",box:"content"},
+ f1X, f1Y, 50, 50);
+ checkQuadIsRect("f1.contentDocument", {toStr:"document",box:"margin"},
+ f1X, f1Y, 50, 50);
+ checkQuadIsRect("f1.contentDocument", {toStr:"document",box:"padding"},
+ f1X, f1Y, 50, 50);
+
+ // Test that anonymous boxes are correctly ignored when building quads.
+ var ibSplitPart1X = ibSplitPart1.getBoundingClientRect().left;
+ var ibSplitY = ibSplit.getBoundingClientRect().top;
+ isEval("ibSplit.getBoxQuads().length", 3);
+ isEval("ibSplit.getBoxQuads()[0].getBounds().left", ibSplitPart1X);
+ isEval("ibSplit.getBoxQuads()[0].getBounds().width", 100);
+ isEval("ibSplit.getBoxQuads()[1].getBounds().width", 110);
+ isEval("ibSplit.getBoxQuads()[2].getBounds().width", 130);
+ isEval("table.getBoxQuads().length", 2);
+ isEval("table.getBoxQuads()[0].getBounds().height", 50);
+ isEval("table.getBoxQuads()[1].getBounds().height", 40);
+
+ // Test that we skip anonymous boxes when finding the right box to be relative to.
+ checkQuadIsRect("d", {toStr:"ibSplit", tolerance:0.0001},
+ dX - ibSplitPart1X, dY - ibSplitY, dW, dH);
+ var tableX = table.getClientRects()[0].left;
+ var tableY = table.getClientRects()[0].top;
+ checkQuadIsRect("d", {toStr:"table"},
+ dX - tableX, dY - tableY, dW, dH);
+ isEval("ibSplit.convertPointFromNode(zeroPoint,d).x", dX - ibSplitPart1X);
+ isEval("table.convertPointFromNode(zeroPoint,d).x", dX - table.getClientRects()[0].left);
+
+ // Test boxes generated by block splitting. Check for borders being placed correctly.
+ var colSplitY = colSplit.getClientRects()[0].top;
+ isEval("colSplit.getBoxQuads().length", 2);
+ isEval("colSplit.getBoxQuads()[0].getBounds().top", colSplitY);
+ isEval("colSplit.getBoxQuads()[0].getBounds().height", 60);
+ isEval("colSplit.getBoxQuads()[1].getBounds().top", colSplitY - 20);
+ isEval("colSplit.getBoxQuads()[1].getBounds().height", 45);
+ isEval("colSplit.getBoxQuads({box:'content'}).length", 2);
+ // The first box for the block has the top border; the second box has the bottom border.
+ isEval("colSplit.getBoxQuads({box:'content'})[0].getBounds().top", colSplitY + 10);
+ isEval("colSplit.getBoxQuads({box:'content'})[0].getBounds().height", 50);
+ isEval("colSplit.getBoxQuads({box:'content'})[1].getBounds().top", colSplitY - 20);
+ isEval("colSplit.getBoxQuads({box:'content'})[1].getBounds().height", 30);
+
+ var inlineSplitX = inlineSplit.getClientRects()[0].left;
+ isEval("inlineSplit.getBoxQuads().length", 2);
+ isEval("inlineSplit.getBoxQuads()[0].getBounds().left", inlineSplitX);
+ isEval("inlineSplit.getBoxQuads()[0].getBounds().width", 30);
+ isEval("inlineSplit.getBoxQuads()[1].getBounds().left", inlineSplitX - 150);
+ isEval("inlineSplit.getBoxQuads()[1].getBounds().width", 75);
+ isEval("inlineSplit.getBoxQuads({box:'content'}).length", 2);
+ // The first box for the inline has the left border; the second box has the right border.
+ isEval("inlineSplit.getBoxQuads({box:'content'})[0].getBounds().left", inlineSplitX + 10);
+ isEval("inlineSplit.getBoxQuads({box:'content'})[0].getBounds().width", 20);
+ isEval("inlineSplit.getBoxQuads({box:'content'})[1].getBounds().left", inlineSplitX - 150);
+ isEval("inlineSplit.getBoxQuads({box:'content'})[1].getBounds().width", 60);
+
+ var textX = textContainer.getClientRects()[0].left;
+ isEval("text.getBoxQuads().length", 2);
+ isEval("text.getBoxQuads()[0].getBounds().left", textX);
+ isEval("text.getBoxQuads()[1].getBounds().left", textX - 150);
+ // Box types are irrelevant for text
+ isEval("text.getBoxQuads({box:'content'}).length", 2);
+ isEval("text.getBoxQuads({box:'content'})[0].getBounds().left", textX);
+ isEval("text.getBoxQuads({box:'content'})[1].getBounds().left", textX - 150);
+ isEval("text.getBoxQuads({box:'padding'}).length", 2);
+ isEval("text.getBoxQuads({box:'padding'})[0].getBounds().left", textX);
+ isEval("text.getBoxQuads({box:'padding'})[1].getBounds().left", textX - 150);
+ isEval("text.getBoxQuads({box:'margin'}).length", 2);
+ isEval("text.getBoxQuads({box:'margin'})[0].getBounds().left", textX);
+ isEval("text.getBoxQuads({box:'margin'})[1].getBounds().left", textX - 150);
+
+ // Test table margins
+ isEval("table.getBoxQuads({box:'margin'}).length", 1);
+ isEval("table.getBoxQuads({box:'margin'})[0].getBounds().height", 106);
+
+ // Check that a text node whose layout might have been optimized away gives
+ // correct results.
+ var suppressedTextContainerX = suppressedTextContainer.getBoundingClientRect().left;
+ isEval("suppressedText.getBoxQuads().length", 1);
+ isEval("suppressedText.getBoxQuads()[0].getBounds().left", suppressedTextContainerX);
+ isEval("suppressedText.getBoxQuads()[0].getBounds().width", 0);
+
+ var suppressedTextContainer2X = suppressedTextContainer2.getBoundingClientRect().left;
+ isEval("document.convertPointFromNode(zeroPoint,suppressedText2).x",
+ suppressedTextContainer2X);
+
+ checkException("comment.getBoxQuads()", "TypeError");
+ checkException("d.getBoxQuads({relativeTo:comment})", "TypeError");
+ checkException("comment.convertPointFromNode(zeroPoint,document)", "TypeError");
+ checkException("document.convertPointFromNode(zeroPoint,comment)", "TypeError");
+ checkException("comment.convertRectFromNode(zeroRect,document)", "TypeError");
+ checkException("document.convertRectFromNode(zeroRect,comment)", "TypeError");
+ checkException("comment.convertQuadFromNode(zeroQuad,document)", "TypeError");
+ checkException("document.convertQuadFromNode(zeroQuad,comment)", "TypeError");
+
+ checkException("fragment.getBoxQuads()", "TypeError");
+ checkException("d.getBoxQuads({relativeTo:fragment})", "TypeError");
+ checkException("fragment.convertPointFromNode(zeroPoint,document)", "TypeError");
+ checkException("document.convertPointFromNode(zeroPoint,fragment)", "TypeError");
+ checkException("fragment.convertRectFromNode(zeroRect,document)", "TypeError");
+ checkException("document.convertRectFromNode(zeroRect,fragment)", "TypeError");
+ checkException("fragment.convertQuadFromNode(zeroQuad,document)", "TypeError");
+ checkException("document.convertQuadFromNode(zeroQuad,fragment)", "TypeError");
+
+ isEval("displayNone.getBoxQuads().length", 0);
+ isEval("notInDocument.getBoxQuads().length", 0);
+ checkNotFound("displayNone", "document", 1, 2, 3, 4);
+ checkNotFound("notInDocument", "document", 1, 2, 3, 4);
+ checkNotFound("document", "displayNone", 1, 2, 3, 4);
+ checkNotFound("document", "notInDocument", 1, 2, 3, 4);
+
+ // Test an overflow:hidden version of d. overflow:hidden should not affect
+ // the quads, basically.
+ var oHX = overflowHidden.getBoundingClientRect().left;
+ var oHY = overflowHidden.getBoundingClientRect().top;
+ checkQuadIsRect("overflowHidden", {box:"content"},
+ oHX + 4 + 8, oHY + 1 + 5, 120, 90);
+ checkQuadIsRect("overflowHidden", {box:"padding"},
+ oHX + 8, oHY + 5, 120 + 2 + 4, 90 + 1 + 3);
+ checkQuadIsRect("overflowHidden", {box:"border"},
+ oHX, oHY, 120 + 2 + 4 + 6 + 8, 90 + 1 + 3 + 5 + 7);
+ checkQuadIsRect("overflowHidden", {},
+ oHX, oHY, 120 + 2 + 4 + 6 + 8, 90 + 1 + 3 + 5 + 7);
+ checkQuadIsRect("overflowHidden", {box:"margin"},
+ oHX - 12, oHY - 9, 120 + 2 + 4 + 6 + 8 + 10 + 12, 90 + 1 + 3 + 5 + 7 + 9 + 11);
+
+ // Test an overflow:scroll version of d. I assume that boxes aren't affected
+ // by the scrollbar although it's not clear that this is correct.
+ var oSX = overflowScroll.getBoundingClientRect().left;
+ var oSY = overflowScroll.getBoundingClientRect().top;
+ checkQuadIsRect("overflowScroll", {box:"content"},
+ oSX + 4 + 8, oSY + 1 + 5, 120, 90);
+ checkQuadIsRect("overflowScroll", {box:"padding"},
+ oSX + 8, oSY + 5, 120 + 2 + 4, 90 + 1 + 3);
+ checkQuadIsRect("overflowScroll", {box:"border"},
+ oSX, oSY, 120 + 2 + 4 + 6 + 8, 90 + 1 + 3 + 5 + 7);
+ checkQuadIsRect("overflowScroll", {},
+ oSX, oSY, 120 + 2 + 4 + 6 + 8, 90 + 1 + 3 + 5 + 7);
+ checkQuadIsRect("overflowScroll", {box:"margin"},
+ oSX - 12, oSY - 9, 120 + 2 + 4 + 6 + 8 + 10 + 12, 90 + 1 + 3 + 5 + 7 + 9 + 11);
+
+ // Test simple 2D transforms.
+ var stcX = scaleTransformContainer.getBoundingClientRect().left;
+ var stcY = scaleTransformContainer.getBoundingClientRect().top;
+ checkQuadIsRect("scaleTransform", {},
+ stcX, stcY, 140, 160);
+ var ttcX = translateTransformContainer.getBoundingClientRect().left;
+ var ttcY = translateTransformContainer.getBoundingClientRect().top;
+ checkQuadIsRect("translateTransform", {},
+ ttcX + 30, ttcY + 40, 70, 80);
+ // Test mapping into a transformed element.
+ checkQuadIsRect("scaleTransform", {toStr:"translateTransform"},
+ stcX - (ttcX + 30), stcY - (ttcY + 40), 140, 160);
+ // Test 90 degree rotation.
+ var rotatetcX = rotateTransformContainer.getBoundingClientRect().left;
+ var rotatetcY = rotateTransformContainer.getBoundingClientRect().top;
+ checkQuadIsQuad("rotateTransform", {},
+ rotatetcX + 75, rotatetcY + 5,
+ rotatetcX + 75, rotatetcY + 75,
+ rotatetcX - 5, rotatetcY + 75,
+ rotatetcX - 5, rotatetcY + 5);
+ // Test vertical flip.
+ var fliptcX = flipTransformContainer.getBoundingClientRect().left;
+ var fliptcY = flipTransformContainer.getBoundingClientRect().top;
+ checkQuadIsQuad("flipTransform", {},
+ fliptcX, fliptcY + 80,
+ fliptcX + 70, fliptcY + 80,
+ fliptcX + 70, fliptcY,
+ fliptcX, fliptcY);
+ // Test non-90deg rotation.
+ var rot45tcX = rot45TransformContainer.getBoundingClientRect().left;
+ var rot45tcY = rot45TransformContainer.getBoundingClientRect().top;
+ var halfDiagonal = 100/Math.sqrt(2);
+ checkQuadIsQuad("rot45Transform", {tolerance:0.01},
+ rot45tcX + 50, rot45tcY + 50 - halfDiagonal,
+ rot45tcX + 50 + halfDiagonal, rot45tcY + 50,
+ rot45tcX + 50, rot45tcY + 50 + halfDiagonal,
+ rot45tcX + 50 - halfDiagonal, rot45tcY + 50);
+
+ // Test singular transforms.
+ var singularTransformX = singularTransform.getBoundingClientRect().left;
+ var singularTransformY = singularTransform.getBoundingClientRect().top;
+ // They map everything to a point.
+ checkQuadIsRect("singularTransform", {},
+ singularTransformX, singularTransformY, 0, 0);
+ checkQuadIsRect("singularTransformChild2", {},
+ singularTransformX, singularTransformY, 0, 0);
+ // Mapping into an element with a singular transform from outside sets
+ // everything to zero.
+ checkQuadIsRect("d", {toStr:"singularTransform"},
+ 0, 0, 0, 0);
+ // But mappings within a subtree of an element with a singular transform work.
+ checkQuadIsRect("singularTransformChild2", {toStr:"singularTransformChild1"},
+ 0, 50, 200, 50);
+
+ // Test 3D transforms.
+ var t3tcX = threeDTransformContainer.getBoundingClientRect().left;
+ var t3tcY = threeDTransformContainer.getBoundingClientRect().top;
+ checkQuadIsQuad("threeDTransform", {tolerance:0.01},
+ t3tcX + 59.446714, t3tcY - 18.569847,
+ t3tcX + 129.570778, t3tcY + 13.540874,
+ t3tcX + 129.570778, t3tcY + 100,
+ t3tcX + 59.446714, t3tcY + 100);
+ // Test nested 3D transforms (without preserve-3d).
+ checkQuadIsQuad("threeDTransformChild", {tolerance:0.01},
+ t3tcX + 89.395061, t3tcY + 2.243033,
+ t3tcX + 113.041727, t3tcY - 2.758530,
+ t3tcX + 113.041727, t3tcY + 52.985921,
+ t3tcX + 89.395061, t3tcY + 47.571899);
+ // Test preserve-3D.
+ var p3dtcX = preserve3DTransformContainer.getBoundingClientRect().left;
+ var p3dtcY = preserve3DTransformContainer.getBoundingClientRect().top;
+ checkQuadIsRect("preserve3DTransformChild", {tolerance:0.01},
+ p3dtcX, p3dtcY, 200, 50,
+ {tolerance:0.0001});
+ // Test mapping back into preserve-3D.
+ checkQuadIsRect("d", {toStr:"preserve3DTransformChild",tolerance:0.01},
+ dX - p3dtcX, dY - p3dtcY, dW, dH);
+
+ // Test SVG.
+ var svgContainerX = svgContainer.getBoundingClientRect().left;
+ var svgContainerY = svgContainer.getBoundingClientRect().top;
+ checkQuadIsRect("circle", {},
+ svgContainerX + 41, svgContainerY + 41, 40, 40);
+ // Box types are ignored for SVG elements.
+ checkQuadIsRect("circle", {box:"content"},
+ svgContainerX + 41, svgContainerY + 41, 40, 40);
+ checkQuadIsRect("circle", {box:"padding"},
+ svgContainerX + 41, svgContainerY + 41, 40, 40);
+ checkQuadIsRect("circle", {box:"margin"},
+ svgContainerX + 41, svgContainerY + 41, 40, 40);
+ checkQuadIsRect("d", {toStr:"circle"},
+ dX - (svgContainerX + 41), dY - (svgContainerY + 41), dW, dH);
+ // Test foreignObject inside an SVG transform.
+ checkQuadIsRect("foreign", {},
+ svgContainerX + 111, svgContainerY + 51, 200, 120);
+ // Outer <svg> elements support padding and content boxes
+ checkQuadIsRect("svg", {box:"border"},
+ svgContainerX, svgContainerY, 222, 222);
+ checkQuadIsRect("svg", {box:"padding"},
+ svgContainerX + 7, svgContainerY + 7, 208, 208);
+ checkQuadIsRect("svg", {box:"content"},
+ svgContainerX + 11, svgContainerY + 11, 200, 200);
+
+ // XXX Test SVG text (probably broken; unclear what the best way is to handle it)
+
+ // Test that converting between nodes in different toplevel browsing contexts
+ // throws an exception.
+ try {
+ openedWindow = window.open("file_getBoxQuads_convertPointRectQuad_frame2.html","");
+ } catch (ex) {
+ // in some cases we can't open the window.
+ openedWindow = null;
+ }
+ if (openedWindow) {
+ openedWindow.addEventListener("load", function() {
+ checkException("openedWindow.d.getBoxQuads({relativeTo:document})", "NotFoundError");
+ checkException("document.getBoxQuads({relativeTo:openedWindow.d})", "NotFoundError");
+ checkException("openedWindow.d.convertPointFromNode(zeroPoint,document)", "NotFoundError");
+ checkException("document.convertPointFromNode(zeroPoint,openedWindow.d)", "NotFoundError");
+ checkException("openedWindow.d.convertRectFromNode(zeroRect,document)", "NotFoundError");
+ checkException("document.convertRectFromNode(zeroRect,openedWindow.d)", "NotFoundError");
+ checkException("openedWindow.d.convertQuadFromNode(zeroQuad,document)", "NotFoundError");
+ checkException("document.convertQuadFromNode(zeroQuad,openedWindow.d)", "NotFoundError");
+ openedWindow.close();
+ SimpleTest.finish();
+ });
+ } else {
+ SimpleTest.finish();
+ }
+}
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_getClientRects_emptytext.html b/layout/base/tests/test_getClientRects_emptytext.html
new file mode 100644
index 0000000000..3717beb0ae
--- /dev/null
+++ b/layout/base/tests/test_getClientRects_emptytext.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<div id="testDiv"> </div>
+<script>
+var textNode = testDiv.firstChild;
+var range = new Range();
+range.selectNodeContents(textNode);
+is(range.getClientRects().length, 1, "Text node should have a rectangle");
+var rect = range.getClientRects()[0];
+ok(rect.left > 0, "Rectangle x should be greater than zero");
+ok(rect.top > 0, "Rectangle y should be greater than zero");
+is(rect.width, 0, "Rectangle should be zero width");
+is(rect.height, 0, "Rectangle should be zero height");
+</script>
+</body>
+</html>
diff --git a/layout/base/tests/test_mozPaintCount.html b/layout/base/tests/test_mozPaintCount.html
new file mode 100644
index 0000000000..dcf8eef3d2
--- /dev/null
+++ b/layout/base/tests/test_mozPaintCount.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Tests for DOMWindowUtils.paintCount</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="doBackgroundFlicker()">
+<p id="display">
+<div width="100" height="100" id="p" style="background-color: rgb(0,0,0)"/>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+var startPaintCount = SpecialPowers.DOMWindowUtils.paintCount;
+ok(true, "Got to initial paint count: " + startPaintCount);
+var color = 0;
+
+function doElementFlicker() {
+ ok(true, "Plugin color iteration " + color + ", paint count: " + SpecialPowers.DOMWindowUtils.paintCount);
+ if (SpecialPowers.DOMWindowUtils.paintCount - startPaintCount > 20) {
+ ok(true, "Got enough paints from plugin color changes");
+ SimpleTest.finish();
+ return;
+ }
+
+ color = (color + 1) % 256;
+ document.getElementById("p").style.backgroundColor = "rgb(" + color + "," + color + "," + color + ")";
+ setTimeout(doElementFlicker, 0);
+}
+
+function doBackgroundFlicker() {
+ ok(true, "Background color iteration " + color + ", paint count: " + SpecialPowers.DOMWindowUtils.paintCount);
+ if (SpecialPowers.DOMWindowUtils.paintCount - startPaintCount > 20) {
+ ok(true, "Got enough paints from background color changes");
+ startPaintCount = SpecialPowers.DOMWindowUtils.paintCount;
+ doElementFlicker();
+ return;
+ }
+
+ color = (color + 1) % 256;
+ document.body.style.backgroundColor = "rgb(" + color + "," + color + "," + color + ")";
+ setTimeout(doBackgroundFlicker, 0);
+}
+
+</script>
+</pre>
+
+<div style="height:4000px"></div>
+<a id="first" href="http://www.mozilla.org/">first<br>link</a>
+<a id="second" href="http://www.mozilla.org/">second link</a>
+<a id="third" href="http://www.mozilla.org/">third<br>link</a>
+<div style="height:4000px"></div>
+
+</body>
+</html>
+
diff --git a/layout/base/tests/test_partialbg.html b/layout/base/tests/test_partialbg.html
new file mode 100644
index 0000000000..8c5b6b466a
--- /dev/null
+++ b/layout/base/tests/test_partialbg.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1231622
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1231622: Draw partial frames of downloading css background images</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="SimpleTest.waitForFocus(runTest)">
+
+<style>
+div {
+ width: 200px;
+ height: 200px;
+ background-size: 200px 200px; background-image: url(sendimagenevercomplete.sjs)
+}
+</style>
+<script>
+/* sendimagenevercomplete.sjs sends us a partial png file and keeps the
+ * connection open but sends no more data. This is enough data to draw at last
+ * a partial frame. We do this so that we can distinguish from drawing a
+ * partial frame after we've been told all data has arrived (what we do even
+ * without the pref layout.display_partial_background_images turned on), from
+ * drawing a partial frame while data is still arriving (what we want to do).
+ */
+
+SimpleTest.waitForExplicitFinish();
+const gUtils = SpecialPowers.getDOMWindowUtils(window);
+
+function checkPixel(r, x, y, red, green, blue, alpha) {
+ let canvas = snapshotRect(window, r);
+ let context = canvas.getContext('2d');
+
+ let image = context.getImageData(x, y, 1, 1);
+ if (image.data[0] == red &&
+ image.data[1] == green &&
+ image.data[2] == blue &&
+ image.data[3] == alpha) {
+ return true;
+ }
+ return false;
+}
+
+async function runTest() {
+ await SpecialPowers.pushPrefEnv({'set': [['layout.display_partial_background_images', true]]});
+
+ let theDiv = document.createElement("div");
+ document.body.appendChild(theDiv);
+
+ let r = theDiv.getBoundingClientRect();
+
+ // Give that some time to partially load.
+ for (let i = 0; i < 10; i++) {
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ }
+
+ let correct = false;
+ while (!correct) {
+ // Check the middle pixel part way down the partial frame.
+ correct = checkPixel(r, 100, 25, 0, 0, 255, 255);
+
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ }
+
+ ok(correct, "correct pixel value");
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_preserve3d_sorting_hit_testing.html b/layout/base/tests/test_preserve3d_sorting_hit_testing.html
new file mode 100644
index 0000000000..2b975a0ef6
--- /dev/null
+++ b/layout/base/tests/test_preserve3d_sorting_hit_testing.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=684759
+-->
+<head>
+ <title>Test for Bug 684759</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=684759">Mozilla Bug 684759</a>
+<iframe src="preserve3d_sorting_hit_testing_iframe.html" id="iframe" height="1000" width="1000" style="border:none"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 684759 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+
+ var iframe = document.getElementById("iframe");
+
+ var doc = iframe.contentDocument;
+
+ var big= doc.getElementById("big");
+ var small = doc.getElementById("small");
+
+ function check(x, y, expected_element, description)
+ {
+ is(doc.elementFromPoint(x, y).id, expected_element.id,
+ "point (" + x + ", " + y + "): " + description);
+ }
+
+ check(650, 250, small, "Small object should be infront of big");
+ check(650, 308, big, "Check bounds of small object");
+ check(650, 207, big, "Check bounds of small object");
+ check(607, 250, big, "Check bounds of small object");
+ check(708, 250, big, "Check bounds of small object");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_preserve3d_sorting_hit_testing2.html b/layout/base/tests/test_preserve3d_sorting_hit_testing2.html
new file mode 100644
index 0000000000..4199907eee
--- /dev/null
+++ b/layout/base/tests/test_preserve3d_sorting_hit_testing2.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1241394
+-->
+<head>
+ <title>Test for Bug 1241394</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1241394">Mozilla Bug 1241394</a>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1241394 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+ var win;
+
+ window.child_opened = function(doc) {
+ var container= doc.getElementById("container");
+
+ isnot(doc.elementFromPoint(60, 50).id, container.id,
+ "point (50, 50): should not hit background");
+
+ win.close();
+ SimpleTest.finish();
+ }
+
+ win = window.open("preserve3d_sorting_hit_testing2_iframe.html");
+}
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_refreshDriver_hasPendingTick.html b/layout/base/tests/test_refreshDriver_hasPendingTick.html
new file mode 100644
index 0000000000..eb92c1fb92
--- /dev/null
+++ b/layout/base/tests/test_refreshDriver_hasPendingTick.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1756269
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1756269: the nsIDOMWindowUtils.refreshDriverHasPendingTick API</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ @keyframes growWidth {
+ from { width: 20px; }
+ to { width: 30px; }
+ }
+ .animating {
+ animation: 1s growWidth infinite alternate;
+ }
+ #sometimesAnimated {
+ background: blue;
+ width: 10px;
+ height: 10px;
+ }
+ </style>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1756269">Mozilla Bug 1756269</a>
+<div id="display">
+ <div id="sometimesAnimated"></div>
+</div>
+<pre id="test">
+<script>
+"use strict";
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("need to allow time to pass so that we can " +
+ "detect unwanted extra refresh driver ticks");
+const gUtils = SpecialPowers.getDOMWindowUtils(window);
+
+async function startAnimAndExpectTick() {
+ // Start an animation:
+ sometimesAnimated.classList.add("animating");
+
+ // double-rAF to flush pending paints:
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+
+ ok(gUtils.refreshDriverHasPendingTick,
+ "Expecting refresh driver to be ticking when animated content is present");
+
+ // clean up, i.e. remove animation:
+ sometimesAnimated.classList.remove("animating");
+
+ // double-rAF to flush pending paints:
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+}
+
+async function expectTicksToStop() {
+ let didStopTicking = false;
+ // Note: The maximum loop count here is an arbitrary large value, just to let
+ // us gracefully handle edge cases where multiple setTimeouts resolve before
+ // a pending refresh driver tick. Really, we just want to be sure the refresh
+ // driver *eventually* stops ticking, and we can do so gracefully by polling
+ // with some generous-but-finite number of checks here.
+ for (var i = 0; i < 100; i++) {
+ await new Promise(r => setTimeout(r, 8));
+ if(!gUtils.refreshDriverHasPendingTick) {
+ didStopTicking = true;
+ break;
+ }
+ }
+ ok(didStopTicking, "refresh driver should have eventually stopped ticking");
+}
+
+async function run() {
+ // By default, the refresh driver ticks on its own for some period of time
+ // after pageload. Turn that off so we don't have to wait it out:
+ await SpecialPowers.pushPrefEnv({'set':
+ [['layout.keep_ticking_after_load_ms', 0]]});
+
+ // Start out with a double-rAF, to flush paints from pageload:
+ await new Promise(r => requestAnimationFrame(r));
+ await new Promise(r => requestAnimationFrame(r));
+
+ await startAnimAndExpectTick();
+ await expectTicksToStop();
+ await startAnimAndExpectTick();
+ await expectTicksToStop();
+
+ SimpleTest.finish();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_reftests_with_caret.html b/layout/base/tests/test_reftests_with_caret.html
new file mode 100644
index 0000000000..0255b5deca
--- /dev/null
+++ b/layout/base/tests/test_reftests_with_caret.html
@@ -0,0 +1,469 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Reftests with caret drawing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/WindowSnapshot.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+ <style>
+ iframe {
+ border: none;
+ width: 600px;
+ height: 400px;
+ }
+ </style>
+<script type="text/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestLongerTimeout(3);
+
+var iframes = [];
+function callbackTestIframe(iframe)
+{
+ iframes.push(iframe);
+
+ if (iframes.length != 2)
+ return;
+
+ var result = iframes[0];
+ var reference = iframes[1];
+
+ // Using assertSnapshots is important to get the data-URIs of failing tests
+ // dumped into the log in a format that reftest-analyzer.xhtml can process.
+ var passed = assertSnapshots(result.snapshot, reference.snapshot, true,
+ null /*no fuzz*/, result.src, reference.src);
+
+ // Remove the iframes if the test was successful
+ if (passed) {
+ result.remove();
+ reference.remove();
+ }
+
+ iframes = [];
+ SimpleTest.waitForFocus(nextTest);
+}
+
+function doSnapShot(iframe) {
+ iframe.snapshot = snapshotWindow(iframe.contentWindow, true);
+ callbackTestIframe(iframe);
+};
+
+function remotePageLoaded(callback) {
+ var iframe = this;
+ setTimeout(function(){
+ doSnapShot(iframe);
+ callback();
+ }, 0);
+};
+
+const MAX_ITERATIONS = 1000;
+
+function createIframe(url,next) {
+ var iframe = document.createElement("iframe");
+ iframe.remotePageLoaded = remotePageLoaded;
+ var me = this;
+ var currentIteration = 0;
+ function iframeLoadCompleted() {
+ let loc = iframe.contentWindow.location;
+ if (loc && loc.href == "about:blank") {
+ return;
+ }
+ var docEl = iframe.contentDocument.documentElement;
+ if (docEl.className.includes("reftest-wait")) {
+ if (currentIteration++ > MAX_ITERATIONS) {
+ ok(false, "iframe load for " + url + " timed out");
+ endTest();
+ } else {
+ setTimeout(iframeLoadCompleted, 0);
+ }
+ return;
+ }
+ iframe.remotePageLoaded(function() {
+ if (next) {
+ setTimeout(function(){createIframe(next,null);}, 0)
+ }
+ });
+ }
+ iframe.addEventListener("load", iframeLoadCompleted);
+ window.document.body.appendChild(iframe);
+ iframe.clientHeight; // flush layout.
+ iframe.src = url;
+ iframe.focus();
+};
+
+function refTest(test,ref) {
+ createIframe(test,ref);
+};
+
+var caretBlinkTime = null;
+async function endTest() {
+ await SpecialPowers.spawn(window.parent, [], () => {
+ content.document.styleSheets[content.document.styleSheets.length-1].deleteRule(0);
+ });
+
+ // finish(), yet let the test actually end first, to be safe.
+ SimpleTest.executeSoon(SimpleTest.finish);
+}
+
+var tests = [
+ [ 'bug106855-1.html' , 'bug106855-1-ref.html' ] ,
+ [ 'bug106855-2.html' , 'bug106855-1-ref.html' ] ,
+ [ 'bug389321-2.html' , 'bug389321-2-ref.html' ] ,
+ [ 'bug613807-1.html' , 'bug613807-1-ref.html' ] ,
+ [ 'bug1082486-1.html', 'bug1082486-1-ref.html'] ,
+ [ 'bug1082486-2.html', 'bug1082486-2-ref.html'] ,
+ // The following test cases uses mouse events. We need to make
+ // AccessibleCaret unhide for them.
+ function() {SpecialPowers.pushPrefEnv({'set': [['layout.accessiblecaret.hide_carets_for_mouse_input', false]]}, nextTest);} ,
+ [ 'bug1516963-1.html', 'bug1516963-1-ref.html'] ,
+ [ 'bug1516963-2.html', 'bug1516963-2-ref.html'] ,
+ [ 'bug1516963-3.html', 'bug1516963-3-ref.html'] ,
+ [ 'bug1516963-4.html', 'bug1516963-4-ref.html'] ,
+ [ 'bug1516963-5.html', 'bug1516963-5-ref.html'] ,
+ [ 'bug1516963-6.html', 'bug1516963-6-ref.html'] ,
+ [ 'bug1550869-1a.html', 'bug1550869-1-ref.html'] ,
+ [ 'bug1550869-1b.html', 'bug1550869-1-ref.html'] ,
+ [ 'bug1550869-2a.html', 'bug1550869-2-ref.html'] ,
+ [ 'bug1550869-2b.html', 'bug1550869-2-ref.html'] ,
+ [ 'bug1550869-2c.html', 'bug1550869-2-ref.html'] ,
+ [ 'bug1550869-2d.html', 'bug1550869-2-ref.html'] ,
+ function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.hide_carets_for_mouse_input']]}, nextTest);} ,
+ // The following test cases are all involving with one sending
+ // synthesizeKey(), the other without. They fail when accessiblecaret
+ // is enabled. Test them with the preference off.
+ function() {SpecialPowers.pushPrefEnv({'set': [['layout.accessiblecaret.enabled_on_touch', false]]}, nextTest);} ,
+ [ 'bug240933-1.html' , 'bug240933-1-ref.html' ] ,
+ [ 'bug240933-2.html' , 'bug240933-1-ref.html' ] ,
+ [ 'bug389321-1.html' , 'bug389321-1-ref.html' ] ,
+ [ 'bug389321-3.html' , 'bug389321-3-ref.html' ] ,
+ [ 'bug482484.html' , 'bug482484-ref.html' ] ,
+ [ 'bug503399.html' , 'bug503399-ref.html' ] ,
+ [ 'bug585922.html' , 'bug585922-ref.html' ] ,
+ [ 'bug597519-1.html' , 'bug597519-1-ref.html' ] ,
+ [ 'bug602141-1.html' , 'bug602141-1-ref.html' ] ,
+ [ 'bug602141-2.html' , 'bug602141-2-ref.html' ] ,
+ [ 'bug602141-3.html' , 'bug602141-3-ref.html' ] ,
+ [ 'bug602141-4.html' , 'bug602141-4-ref.html' ] ,
+ [ 'bug612271-1.html' , 'bug612271-ref.html' ] ,
+ [ 'bug612271-2.html' , 'bug612271-ref.html' ] ,
+ [ 'bug612271-3.html' , 'bug612271-ref.html' ] ,
+ [ 'bug613433-1.html' , 'bug613433-ref.html' ] ,
+ [ 'bug613433-2.html' , 'bug613433-ref.html' ] ,
+ [ 'bug613433-3.html' , 'bug613433-ref.html' ] ,
+ [ 'bug632215-1.html' , 'bug632215-ref.html' ] ,
+ [ 'bug632215-2.html' , 'bug632215-ref.html' ] ,
+ [ 'bug633044-1.html' , 'bug633044-1-ref.html' ] ,
+ [ 'bug634406-1.html' , 'bug634406-1-ref.html' ] ,
+ [ 'bug644428-1.html' , 'bug644428-1-ref.html' ] ,
+ [ 'input-maxlength-valid-before-change.html', 'input-valid-ref.html'] ,
+ [ 'input-maxlength-valid-change.html', 'input-valid-ref.html'] ,
+ [ 'input-maxlength-invalid-change.html', 'input-invalid-ref.html'] ,
+ [ 'input-minlength-valid-before-change.html', 'input-valid-ref.html'] ,
+ [ 'input-minlength-valid-change.html', 'input-valid-ref.html'] ,
+ [ 'input-minlength-invalid-change.html', 'input-invalid-ref.html'] ,
+ [ 'input-maxlength-ui-valid-change.html', 'input-valid-ref.html'] ,
+ [ 'input-maxlength-ui-invalid-change.html', 'input-invalid-ref.html'] ,
+ [ 'input-minlength-ui-valid-change.html', 'input-valid-ref.html'] ,
+ [ 'input-minlength-ui-invalid-change.html', 'input-invalid-ref.html'] ,
+
+ function() {SpecialPowers.pushPrefEnv({'set': [['layout.forms.reveal-password-button.enabled', false]]}, nextTest);} ,
+ [ 'input-password-remask.html', 'input-password-remask-ref.html' ],
+
+ // The following hash means language.
+ function() {SpecialPowers.pushPrefEnv({'set': [['editor.password.mask_delay', 0]]}, nextTest);} ,
+ [ 'input-password-RTL-input.html#arabic', 'input-password-RTL-input-ref.html'],
+ [ 'input-password-RTL-input.html#hebrew', 'input-password-RTL-input-ref.html'],
+ function() {SpecialPowers.pushPrefEnv({'clear': [['editor.password.mask_delay']]}, nextTest);} ,
+
+ // The following hash means "text ('_' is a space)" - unmarsk start - unmask end - selection start - selection end[ - text-transform].
+ [ 'input-password-unmask.html#abc_def-0-7-7-7', 'input-password-unmask-ref.html#abc_def-0-7-7-7'],
+ [ 'input-password-unmask.html#abc_def-0-7-7-7-capitalize', 'input-password-unmask-ref.html#Abc_Def-0-7-7-7'],
+ [ 'input-password-unmask.html#abc_def-0-7-7-7-uppercase', 'input-password-unmask-ref.html#ABC_DEF-0-7-7-7'],
+ [ 'input-password-unmask.html#ABC_DEF-0-7-7-7-lowercase', 'input-password-unmask-ref.html#abc_def-0-7-7-7'],
+ [ 'input-password-unmask.html#abc_def-0-1-1-1', 'input-password-unmask-ref.html#abc_def-0-1-1-1'],
+ [ 'input-password-unmask.html#abc_def-2-4-1-5', 'input-password-unmask-ref.html#abc_def-2-4-1-5'],
+ [ 'input-password-unmask.html#abc_def-6-7-7-7', 'input-password-unmask-ref.html#abc_def-6-7-7-7'],
+
+ // The following hash means unmarsk start - unmask end - selection start - selection end.
+ // The value is "a&#x1f914;b" so that the range is 0-4.
+ [ 'input-password-unmask-around-emoji.html#0-4-4-4', 'input-password-unmask-around-emoji-ref.html#0-4-4-4'],
+ [ 'input-password-unmask-around-emoji.html#0-3-3-3', 'input-password-unmask-around-emoji-ref.html#0-3-3-3'],
+ [ 'input-password-unmask-around-emoji.html#0-1-3-3', 'input-password-unmask-around-emoji-ref.html#0-1-3-3'],
+ [ 'input-password-unmask-around-emoji.html#1-2-3-3', 'input-password-unmask-around-emoji-ref.html#1-2-3-3'],
+ [ 'input-password-unmask-around-emoji.html#1-3-3-3', 'input-password-unmask-around-emoji-ref.html#1-3-3-3'],
+ [ 'input-password-unmask-around-emoji.html#2-3-3-3', 'input-password-unmask-around-emoji-ref.html#2-3-3-3'],
+ [ 'input-password-unmask-around-emoji.html#3-4-4-4', 'input-password-unmask-around-emoji-ref.html#3-4-4-4'],
+ [ 'input-password-unmask-around-emoji.html#1-4-4-4', 'input-password-unmask-around-emoji-ref.html#1-4-4-4'],
+ function() {SpecialPowers.pushPrefEnv({'clear': [['layout.forms.reveal-password-button.enabled']]}, nextTest);} ,
+
+ // The following tests something in editable elements so that we need to disable zoom to focused editor.
+ function() {SpecialPowers.pushPrefEnv({'set': [['apz.zoom-to-focused-input.enabled', false]]}, nextTest);} ,
+ [ 'bug956530-1.html' , 'bug956530-1-ref.html' ] , // Clicks an <input> element
+ [ 'bug1097242-1.html', 'bug1097242-1-ref.html'] , // Clicks non-editable and non-selectable element in an editing host
+ [ 'bug1423331-1.html' , 'bug1423331-1-ref.html' ] , // Clicks in an editing host
+ [ 'bug1423331-2.html' , 'bug1423331-2-ref.html' ] , // Clicks in an editing host
+ [ 'bug1506547-4.html' , 'bug1506547-4-ref.html' ] , // Clicks in an editing host
+ [ 'bug1506547-5.html' , 'bug1506547-5-ref.html' ] , // Clicks in an editing host
+ [ 'bug1506547-6.html' , 'bug1506547-5-ref.html' ] , // Clicks in an editing host (reference)
+ [ 'bug1518339-1.html' , 'bug1518339-1-ref.html' ] , // Clicks in an editing host
+ [ 'bug1518339-2.html' , 'bug1518339-2-ref.html' ] , // Clicks in an editing host
+ function() {SpecialPowers.pushPrefEnv({'clear': [['apz.zoom-to-focused-input.enabled']]}, nextTest);} ,
+
+ [ 'input-stoppropagation.html', 'input-stoppropagation-ref.html'] ,
+ [ 'textarea-maxlength-valid-before-change.html', 'textarea-valid-ref.html'] ,
+ [ 'textarea-maxlength-valid-change.html', 'textarea-valid-ref.html'] ,
+ [ 'textarea-maxlength-invalid-change.html', 'textarea-invalid-ref.html'] ,
+ [ 'textarea-minlength-valid-before-change.html', 'textarea-valid-ref.html'] ,
+ [ 'textarea-minlength-valid-change.html', 'textarea-valid-ref.html'] ,
+ [ 'textarea-minlength-invalid-change.html', 'textarea-invalid-ref.html'] ,
+ [ 'textarea-maxlength-ui-valid-change.html', 'textarea-valid-ref.html'] ,
+ [ 'textarea-maxlength-ui-invalid-change.html', 'textarea-invalid-ref.html'] ,
+ [ 'textarea-minlength-ui-valid-change.html', 'textarea-valid-ref.html'] ,
+ [ 'textarea-minlength-ui-invalid-change.html', 'textarea-invalid-ref.html'] ,
+ function() {SpecialPowers.pushPrefEnv({'set': [['bidi.browser.ui', true]]}, nextTest);} ,
+ [ 'bug646382-1.html' , 'bug646382-1-ref.html' ] ,
+ [ 'bug646382-2.html' , 'bug646382-2-ref.html' ] ,
+ [ 'bug664087-1.html' , 'bug664087-1-ref.html' ] ,
+ [ 'bug664087-2.html' , 'bug664087-2-ref.html' ] ,
+ [ 'bug682712-1.html' , 'bug682712-1-ref.html' ] ,
+ function() {SpecialPowers.pushPrefEnv({'clear': [['bidi.browser.ui']]}, nextTest);} ,
+ [ 'bug746993-1.html' , 'bug746993-1-ref.html' ] ,
+ function() {SpecialPowers.pushPrefEnv({'set': [['layout.css.overflow-clip-box.enabled', true]]}, nextTest);} ,
+ [ 'bug966992-1.html' , 'bug966992-1-ref.html' ] ,
+ [ 'bug966992-2.html' , 'bug966992-2-ref.html' ] ,
+ [ 'bug966992-3.html' , 'bug966992-3-ref.html' ] ,
+ function() {SpecialPowers.pushPrefEnv({'clear': [['layout.css.overflow-clip-box.enabled']]}, nextTest);} ,
+ [ 'bug989012-1.html' , 'bug989012-1-ref.html' ] ,
+ [ 'bug989012-2.html' , 'bug989012-2-ref.html' ] ,
+ [ 'bug989012-3.html' , 'bug989012-3-ref.html' ] ,
+ [ 'bug1007065-1.html' , 'bug1007065-1-ref.html' ] ,
+ [ 'bug1007067-1.html' , 'bug1007067-1-ref.html' ] ,
+ [ 'bug1061468.html' , 'bug1061468-ref.html' ] ,
+ [ 'bug1109968-1.html', 'bug1109968-1-ref.html'] ,
+ [ 'bug1109968-2.html', 'bug1109968-2-ref.html'] ,
+ // [ 'bug1123067-1.html' , 'bug1123067-ref.html' ] , TODO: bug 1129205
+ [ 'bug1123067-2.html' , 'bug1123067-ref.html' ] ,
+ [ 'bug1123067-3.html' , 'bug1123067-ref.html' ] ,
+ [ 'bug1132768-1.html' , 'bug1132768-1-ref.html'] ,
+ [ 'bug1237236-1.html' , 'bug1237236-1-ref.html' ] ,
+ [ 'bug1237236-2.html' , 'bug1237236-2-ref.html' ] ,
+ [ 'bug1258308-1.html' , 'bug1258308-1-ref.html' ] ,
+ [ 'bug1258308-2.html' , 'bug1258308-2-ref.html' ] ,
+ [ 'bug1259949-1.html' , 'bug1259949-1-ref.html'] ,
+ [ 'bug1259949-2.html' , 'bug1259949-2-ref.html'] ,
+ [ 'bug1263288.html' , 'bug1263288-ref.html'] ,
+ [ 'bug1263357-1.html' , 'bug1263357-1-ref.html'] ,
+ [ 'bug1263357-2.html' , 'bug1263357-2-ref.html'] ,
+ [ 'bug1263357-3.html' , 'bug1263357-3-ref.html'] ,
+ [ 'bug1263357-4.html' , 'bug1263357-4-ref.html'] ,
+ [ 'bug1263357-5.html' , 'bug1263357-5-ref.html'] ,
+ [ 'bug1354478-1.html' , 'bug1354478-1-ref.html'] ,
+ [ 'bug1354478-2.html' , 'bug1354478-2-ref.html'] ,
+ [ 'bug1354478-3.html' , 'bug1354478-3-ref.html'] ,
+ [ 'bug1354478-4.html' , 'bug1354478-4-ref.html'] ,
+ [ 'bug1354478-5.html' , 'bug1354478-5-ref.html'] ,
+ [ 'bug1354478-6.html' , 'bug1354478-6-ref.html'] ,
+ [ 'bug1359411.html' , 'bug1359411-ref.html' ] ,
+ [ 'bug1415416.html' , 'bug1415416-ref.html' ] ,
+ // FIXME(bug 1434949): These two fail in some platforms.
+ // [ 'bug1423331-3.html' , 'bug1423331-1-ref.html' ] ,
+ // [ 'bug1423331-4.html' , 'bug1423331-2-ref.html' ] ,
+ [ 'bug1484094-1.html' , 'bug1484094-1-ref.html' ] ,
+ [ 'bug1484094-2.html' , 'bug1484094-2-ref.html' ] ,
+ [ 'bug1506547-1.html' , 'bug1506547-2.html' ] ,
+ [ 'bug1506547-2.html' , 'bug1506547-3.html' ] ,
+ [ 'bug1510942-1.html' , 'bug1510942-1-ref.html' ] ,
+ [ 'bug1510942-2.html' , 'bug1510942-2-ref.html' ] ,
+ [ 'bug1524266-1.html' , 'bug1524266-1-ref.html' ] ,
+ // Checks that the caret isn't occluded by children background content.
+ [ 'bug1591282-1.html' , 'bug1591282-1-ref.html' ] ,
+ // Caret on contenteditable with abspos and / or empty content.
+ [ 'bug1634543-1.html' , 'bug1634543-1-ref.html' ] ,
+ [ 'bug1634543-2.html' , 'bug1634543-1-ref.html' ] ,
+ // TODO(emilio): This fails because nsInlineFrame::GetCaretBaseline doesn't
+ // return one line-height for an empty inline, and it probably should..
+ // [ 'bug1634543-3.html' , 'bug1634543-1-ref.html' ] ,
+ [ 'bug1634543-4.html' , 'bug1634543-1-ref.html' ] ,
+ // Caret + line-height + pseudo-element only.
+ [ 'bug1634743-1.html' , 'bug1634743-1-ref.html' ] ,
+ [ 'bug1637476-1.html' , 'bug1637476-1-ref.html' ] ,
+ [ 'bug1637476-2.html' , 'bug1637476-2-ref.html' ] ,
+ [ 'bug1637476-3.html' , 'bug1637476-3-ref.html' ] ,
+ [ 'bug1663475-1.html' , 'bug1663475-1-ref.html' ] ,
+ [ 'bug1663475-2.html' , 'bug1663475-2-ref.html' ] ,
+ // shift+arrow key should select non-editable only
+ [ 'bug1670531-1.html' , 'bug1670531-2.html' ] ,
+ [ 'bug1670531-3.html' , 'bug1670531-3-ref.html' ] ,
+ [ 'bug1670531-4.html' , 'bug1670531-3-ref.html' ] ,
+ function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.enabled_on_touch']]}, nextTest);} ,
+ function() {SpecialPowers.pushPrefEnv({'set': [['accessibility.browsewithcaret', true]]}, nextTest);} ,
+ [ 'bug1529492-1.html' , 'bug1529492-1-ref.html' ] ,
+ function() {SpecialPowers.pushPrefEnv({'clear': [['accessibility.browsewithcaret']]}, nextTest);} ,
+ [ 'interlinePosition-after-Selection-addRange.html', 'interlinePosition-after-Selection-addRange-ref.html' ] ,
+ [ 'collapse-selection-into-editing-host-during-blur-of-input.html', 'collapse-selection-into-editing-host-during-blur-of-input-ref.html' ] ,
+];
+
+if (!navigator.appVersion.includes("Android")) {
+ tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // Tests spellchecker
+ tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // Tests spellchecker
+ tests.push([ 'bug923376.html' , 'bug923376-ref.html' ]); // Tests spellchecker
+ tests.push([ 'bug1496118.html' , 'bug1496118-ref.html' ]); // Tests DnD
+} else {
+ is(SpecialPowers.getIntPref("layout.spellcheckDefault"), 0, "Spellcheck should be turned off for this platform or this if..else check removed");
+}
+
+if (navigator.platform.includes("Linux") && !navigator.appVersion.includes("Android")) {
+ tests = tests.concat([
+ // Turn off accessiblecaret to prevent it from interfering with the
+ // multi-range selection.
+ function() {SpecialPowers.pushPrefEnv({'set': [['layout.accessiblecaret.enabled_on_touch', false]]}, nextTest);} ,
+ // eDirPrevious, Shift+click
+ [ 'multi-range-user-select.html#prev1S_' , 'multi-range-user-select-ref.html#prev1S_' ] ,
+ [ 'multi-range-user-select.html#prev2S_' , 'multi-range-user-select-ref.html#prev2S_' ] ,
+ [ 'multi-range-user-select.html#prev3S_' , 'multi-range-user-select-ref.html#prev3S_' ] ,
+ [ 'multi-range-user-select.html#prev4S_' , 'multi-range-user-select-ref.html#prev4S_' ] ,
+ [ 'multi-range-user-select.html#prev5S_' , 'multi-range-user-select-ref.html#prev5S_' ] ,
+ [ 'multi-range-user-select.html#prev6S_' , 'multi-range-user-select-ref.html#prev6S_' ] ,
+ [ 'multi-range-user-select.html#prev7S_' , 'multi-range-user-select-ref.html#prev7S_' ] ,
+ // eDirPrevious, Shift+Accel+click
+ [ 'multi-range-user-select.html#prev1SA' , 'multi-range-user-select-ref.html#prev1SA' ] ,
+ [ 'multi-range-user-select.html#prev2SA' , 'multi-range-user-select-ref.html#prev2SA' ] ,
+ [ 'multi-range-user-select.html#prev3SA' , 'multi-range-user-select-ref.html#prev3SA' ] ,
+ [ 'multi-range-user-select.html#prev4SA' , 'multi-range-user-select-ref.html#prev4SA' ] ,
+ [ 'multi-range-user-select.html#prev5SA' , 'multi-range-user-select-ref.html#prev5SA' ] ,
+ [ 'multi-range-user-select.html#prev6SA' , 'multi-range-user-select-ref.html#prev6SA' ] ,
+ [ 'multi-range-user-select.html#prev7SA' , 'multi-range-user-select-ref.html#prev7SA' ] ,
+ // eDirPrevious, Accel+drag-select (adding an additional range)
+ [ 'multi-range-user-select.html#prev1AD' , 'multi-range-user-select-ref.html#prev1AD' ] ,
+ [ 'multi-range-user-select.html#prev7AD' , 'multi-range-user-select-ref.html#prev7AD' ] ,
+ // eDirPrevious, Accel+drag-select (bug 1128722)
+ [ 'multi-range-user-select.html#prev8AD' , 'multi-range-user-select-ref.html#prev8AD' ] ,
+ // eDirPrevious, VK_RIGHT / LEFT
+ [ 'multi-range-user-select.html#prev1SR' , 'multi-range-user-select-ref.html#prev1SR' ] ,
+ [ 'multi-range-user-select.html#prev1SL' , 'multi-range-user-select-ref.html#prev1SL' ] ,
+ // eDirNext, Shift+click
+ [ 'multi-range-user-select.html#next1S_' , 'multi-range-user-select-ref.html#next1S_' ] ,
+ [ 'multi-range-user-select.html#next2S_' , 'multi-range-user-select-ref.html#next2S_' ] ,
+ [ 'multi-range-user-select.html#next3S_' , 'multi-range-user-select-ref.html#next3S_' ] ,
+ [ 'multi-range-user-select.html#next4S_' , 'multi-range-user-select-ref.html#next4S_' ] ,
+ [ 'multi-range-user-select.html#next5S_' , 'multi-range-user-select-ref.html#next5S_' ] ,
+ [ 'multi-range-user-select.html#next6S_' , 'multi-range-user-select-ref.html#next6S_' ] ,
+ [ 'multi-range-user-select.html#next7S_' , 'multi-range-user-select-ref.html#next7S_' ] ,
+ // eDirNext, Shift+Accel+click
+ [ 'multi-range-user-select.html#next1SA' , 'multi-range-user-select-ref.html#next1SA' ] ,
+ [ 'multi-range-user-select.html#next2SA' , 'multi-range-user-select-ref.html#next2SA' ] ,
+ [ 'multi-range-user-select.html#next3SA' , 'multi-range-user-select-ref.html#next3SA' ] ,
+ [ 'multi-range-user-select.html#next4SA' , 'multi-range-user-select-ref.html#next4SA' ] ,
+ [ 'multi-range-user-select.html#next5SA' , 'multi-range-user-select-ref.html#next5SA' ] ,
+ [ 'multi-range-user-select.html#next6SA' , 'multi-range-user-select-ref.html#next6SA' ] ,
+ [ 'multi-range-user-select.html#next7SA' , 'multi-range-user-select-ref.html#next7SA' ] ,
+ // eDirNext, Accel+drag-select (adding an additional range)
+ [ 'multi-range-user-select.html#next1AD' , 'multi-range-user-select-ref.html#next1AD' ] ,
+ [ 'multi-range-user-select.html#next7AD' , 'multi-range-user-select-ref.html#next7AD' ] ,
+ // eDirNext, Accel+drag-select (bug 1128722)
+ [ 'multi-range-user-select.html#next8AD' , 'multi-range-user-select-ref.html#next8AD' ] ,
+ // eDirNext, VK_RIGHT / LEFT
+ [ 'multi-range-user-select.html#next1SR' , 'multi-range-user-select-ref.html#next1SR' ] ,
+ [ 'multi-range-user-select.html#next1SL' , 'multi-range-user-select-ref.html#next1SL' ] ,
+ // eDirPrevious, Shift+click
+ [ 'multi-range-script-select.html#prev1S_' , 'multi-range-script-select-ref.html#prev1S_' ] ,
+ [ 'multi-range-script-select.html#prev2S_' , 'multi-range-script-select-ref.html#prev2S_' ] ,
+ [ 'multi-range-script-select.html#prev3S_' , 'multi-range-script-select-ref.html#prev3S_' ] ,
+ [ 'multi-range-script-select.html#prev4S_' , 'multi-range-script-select-ref.html#prev4S_' ] ,
+ [ 'multi-range-script-select.html#prev5S_' , 'multi-range-script-select-ref.html#prev5S_' ] ,
+ [ 'multi-range-script-select.html#prev6S_' , 'multi-range-script-select-ref.html#prev6S_' ] ,
+ [ 'multi-range-script-select.html#prev7S_' , 'multi-range-script-select-ref.html#prev7S_' ] ,
+ // eDirPrevious, Shift+Accel+click
+ [ 'multi-range-script-select.html#prev1SA' , 'multi-range-script-select-ref.html#prev1SA' ] ,
+ [ 'multi-range-script-select.html#prev2SA' , 'multi-range-script-select-ref.html#prev2SA' ] ,
+ [ 'multi-range-script-select.html#prev3SA' , 'multi-range-script-select-ref.html#prev3SA' ] ,
+ [ 'multi-range-script-select.html#prev4SA' , 'multi-range-script-select-ref.html#prev4SA' ] ,
+ [ 'multi-range-script-select.html#prev5SA' , 'multi-range-script-select-ref.html#prev5SA' ] ,
+ [ 'multi-range-script-select.html#prev6SA' , 'multi-range-script-select-ref.html#prev6SA' ] ,
+ [ 'multi-range-script-select.html#prev7SA' , 'multi-range-script-select-ref.html#prev7SA' ] ,
+ // eDirPrevious, Accel+drag-select (adding an additional range)
+ [ 'multi-range-script-select.html#prev1AD' , 'multi-range-script-select-ref.html#prev1AD' ] ,
+ [ 'multi-range-script-select.html#prev7AD' , 'multi-range-script-select-ref.html#prev7AD' ] ,
+ // eDirPrevious, VK_RIGHT / LEFT
+ [ 'multi-range-script-select.html#prev1SR' , 'multi-range-script-select-ref.html#prev1SR' ] ,
+ [ 'multi-range-script-select.html#prev1SL' , 'multi-range-script-select-ref.html#prev1SL' ] ,
+ // eDirNext, Shift+click
+ [ 'multi-range-script-select.html#next1S_' , 'multi-range-script-select-ref.html#next1S_' ] ,
+ [ 'multi-range-script-select.html#next2S_' , 'multi-range-script-select-ref.html#next2S_' ] ,
+ [ 'multi-range-script-select.html#next3S_' , 'multi-range-script-select-ref.html#next3S_' ] ,
+ [ 'multi-range-script-select.html#next4S_' , 'multi-range-script-select-ref.html#next4S_' ] ,
+ [ 'multi-range-script-select.html#next5S_' , 'multi-range-script-select-ref.html#next5S_' ] ,
+ [ 'multi-range-script-select.html#next6S_' , 'multi-range-script-select-ref.html#next6S_' ] ,
+ [ 'multi-range-script-select.html#next7S_' , 'multi-range-script-select-ref.html#next7S_' ] ,
+ // eDirNext, Shift+Accel+click
+ [ 'multi-range-script-select.html#next1SA' , 'multi-range-script-select-ref.html#next1SA' ] ,
+ [ 'multi-range-script-select.html#next2SA' , 'multi-range-script-select-ref.html#next2SA' ] ,
+ [ 'multi-range-script-select.html#next3SA' , 'multi-range-script-select-ref.html#next3SA' ] ,
+ [ 'multi-range-script-select.html#next4SA' , 'multi-range-script-select-ref.html#next4SA' ] ,
+ [ 'multi-range-script-select.html#next5SA' , 'multi-range-script-select-ref.html#next5SA' ] ,
+ [ 'multi-range-script-select.html#next6SA' , 'multi-range-script-select-ref.html#next6SA' ] ,
+ [ 'multi-range-script-select.html#next7SA' , 'multi-range-script-select-ref.html#next7SA' ] ,
+ // eDirNext, Accel+drag-select (adding an additional range)
+ [ 'multi-range-script-select.html#next1AD' , 'multi-range-script-select-ref.html#next1AD' ] ,
+ [ 'multi-range-script-select.html#next7AD' , 'multi-range-script-select-ref.html#next7AD' ] ,
+ // eDirNext, VK_RIGHT / LEFT
+ [ 'multi-range-script-select.html#next1SR' , 'multi-range-script-select-ref.html#next1SR' ] ,
+ [ 'multi-range-script-select.html#next1SL' , 'multi-range-script-select-ref.html#next1SL' ] ,
+
+ // Tries to select and delete non-selectable content in a user-select subtree.
+ [ 'bug1524266-2.html' , 'bug1524266-2-ref.html' ] ,
+ [ 'bug1524266-3.html' , 'bug1524266-2-ref.html' ] ,
+ // Tries to select and delete non-editable content in a user-select subtree.
+ [ 'bug1524266-4.html' , 'bug1524266-2-ref.html' ] ,
+ // Tries to edit an <input type="number"> with arrows inside a user-select: none element.
+ [ 'bug1611661.html' , 'bug1611661-ref.html' ] ,
+ function() {SpecialPowers.pushPrefEnv({'clear': [['layout.accessiblecaret.enabled_on_touch']]}, nextTest);} ,
+ ]);
+}
+
+var testIndex = 0;
+
+// Change it to something like /bug1524266/ to skip all other tests and make
+// debugging easier...
+const DEBUG_TEST_FILTER = null;
+
+function nextTest() {
+ if (testIndex < tests.length) {
+ let test = tests[testIndex];
+ if (typeof(test) == 'function') {
+ test();
+ } else if (!DEBUG_TEST_FILTER || DEBUG_TEST_FILTER.test(test[0])) {
+ refTest(test[0], test[1]);
+ } else {
+ setTimeout(nextTest, 0);
+ }
+ ++testIndex;
+ } else {
+ endTest();
+ }
+}
+async function runTests() {
+ try {
+ if (window.parent) {
+ await SpecialPowers.spawn(window.parent, [], () => {
+ content.document.styleSheets[content.document.styleSheets.length-1]
+ .insertRule("iframe#testframe{width:600px;height:400px}",0);
+ });
+ }
+ try {
+ caretBlinkTime = SpecialPowers.getIntPref("ui.caretBlinkTime");
+ } catch (e) {}
+ SpecialPowers.pushPrefEnv({'set': [['ui.caretBlinkTime', -1]]}, nextTest);
+ } catch(e) {
+ endTest();
+ }
+}
+
+SimpleTest.waitForFocus(runTests);
+
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/layout/base/tests/test_resize_flush.html b/layout/base/tests/test_resize_flush.html
new file mode 100644
index 0000000000..ad14a8f700
--- /dev/null
+++ b/layout/base/tests/test_resize_flush.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1279202
+-->
+<head>
+ <title>Test for Bug 1279202</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="run()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1279202">Mozilla Bug 1279202</a>
+<iframe src="resize_flush_iframe.html" id="iframe" height="200" width="200" style="border:none"></iframe>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1279202 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function run() {
+
+ var iframe = document.getElementById("iframe");
+ var doc = iframe.contentDocument.documentElement;
+ var win = iframe.contentWindow;
+ var body = iframe.contentDocument.body;
+
+ // Flush any pending layout changes before we start.
+ var width = doc.clientWidth;
+
+ // Resize the iframe
+ iframe.width = '300px';
+
+ // Flush pending style changes, but not layout ones. We do this twice because the first flush
+ // does a partial flush of the resize (setting the size on the pres context) which sets the
+ // need style flush flag again. The second call makes sure mNeedStyleFlush is false.
+ var color = win.getComputedStyle(body).getPropertyValue("background-color");
+ color = win.getComputedStyle(body).getPropertyValue("background-color");
+ is(color, "rgb(0, 128, 0)", "Style flush not completed when resizing an iframe!");
+
+ // Query the size of the inner document and make sure it has had a layout flush.
+ width = doc.clientWidth;
+
+ is(width, 300, "Layout flush not completed when resizing an iframe!");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_scroll_event_ordering.html b/layout/base/tests/test_scroll_event_ordering.html
new file mode 100644
index 0000000000..9626d6bb6b
--- /dev/null
+++ b/layout/base/tests/test_scroll_event_ordering.html
@@ -0,0 +1,63 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=785588
+-->
+<head>
+ <title>Test for Bug 785588 --- ordering of scroll-related events</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.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=785588">Mozilla Bug 785588</a>
+<div id="content">
+ <div id="d" style="border:2px solid black; width:100px; height:100px; overflow:auto">
+ <div id="inner" style="height:200px;">Hello</div>
+ </div>
+</div>
+<pre id="test">
+<script>
+SimpleTest.waitForExplicitFinish();
+
+var smoothScrollPref = "general.smoothScroll";
+
+var d = document.getElementById("d");
+d.scrollTop = 0;
+var inner = document.getElementById("inner");
+
+var state = "initial";
+
+function onFrame() {
+ is(state, "didOnScroll", "Must have got scroll event already");
+ state = "didOnFrame";
+ SimpleTest.finish();
+}
+
+function onScroll() {
+ is(state, "initial", "Must be in initial state");
+ ok(d.scrollTop > 0, "Must have scrolled by some amount (got " + d.scrollTop + ")");
+ state = "didOnScroll";
+}
+
+function doTest() {
+ window.getSelection().collapse(inner.firstChild, 0);
+ window.requestAnimationFrame(onFrame);
+ d.onscroll = onScroll;
+ d.scroll(0, 100);
+}
+
+function prepareTest() {
+ // Start the test after we've gotten at least one rAF callback, to make sure
+ // that rAF is no longer throttled. (See bug 1145439.)
+ window.requestAnimationFrame(function() {
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, doTest);
+ });
+}
+
+SimpleTest.waitForFocus(prepareTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_scroll_into_view_in_oopif.html b/layout/base/tests/test_scroll_into_view_in_oopif.html
new file mode 100644
index 0000000000..7264a20d62
--- /dev/null
+++ b/layout/base/tests/test_scroll_into_view_in_oopif.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<style>
+#scroller {
+ width: 300px;
+ height: 300px;
+ overflow: scroll;
+}
+#spacer {
+ width: 100%;
+ height: 1000px;
+}
+</style>
+<div id="scroller">
+ <div id="spacer"></div>
+ <iframe id="iframe"></iframe>
+</div>
diff --git a/layout/base/tests/test_scroll_selection_into_view.html b/layout/base/tests/test_scroll_selection_into_view.html
new file mode 100644
index 0000000000..cbd9db015b
--- /dev/null
+++ b/layout/base/tests/test_scroll_selection_into_view.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for scrolling selection into view</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var ANCHOR = 0;
+var FOCUS = 1;
+var win;
+
+function testCollapsed(id, vPercent, startAt, expected) {
+ var selection = SpecialPowers.wrap(win.getSelection());
+
+ var c = win.document.getElementById("c" + id);
+ var target = win.document.getElementById("target" + id);
+ if (target.contentDocument) {
+ selection = SpecialPowers.wrap(target.contentWindow.getSelection());
+ target = target.contentDocument.getElementById("target" + id);
+ }
+ selection.collapse(target.parentNode, 0);
+ c.scrollTop = startAt;
+ selection.scrollIntoView(FOCUS, true, vPercent, 0);
+ is(c.scrollTop, expected, "Scrolling " + target.id +
+ " into view with vPercent " + vPercent + ", starting at " + startAt);
+}
+
+function doTest() {
+ // Test scrolling an element smaller than the scrollport
+ testCollapsed("1", 0, 0, 400);
+ testCollapsed("1", 100, 0, 220);
+ testCollapsed("1", -1, 0, 220);
+ testCollapsed("1", 0, 500, 400);
+ testCollapsed("1", 100, 500, 220);
+ testCollapsed("1", -1, 500, 400);
+
+ // overflow:hidden elements should not be scrolled by selection
+ // scrolling-into-view
+ testCollapsed("2", 0, 0, 0);
+ testCollapsed("2", 100, 0, 0);
+ testCollapsed("2", -1, 0, 0);
+ testCollapsed("2", 0, 500, 500);
+ testCollapsed("2", 100, 500, 500);
+ testCollapsed("2", -1, 500, 500);
+
+ // Test scrolling an element larger than the scrollport
+ testCollapsed("3", 0, 0, 400);
+ testCollapsed("3", 100, 0, 500);
+ testCollapsed("3", -1, 0, 400);
+ testCollapsed("3", 0, 1000, 400);
+ testCollapsed("3", 100, 1000, 500);
+ // If the element can't be completely visible, show as much as possible,
+ // and don't hide anything which was initially visible.
+ testCollapsed("3", -1, 1000, 500);
+
+ // Test scrolling an element larger than the scrollport
+ testCollapsed("4", 0, 0, 400);
+ testCollapsed("4", 100, 0, 500);
+ testCollapsed("4", -1, 0, 400);
+ testCollapsed("4", 0, 1000, 400);
+ testCollapsed("4", 100, 1000, 500);
+ // If the element can't be completely visible, show as much as possible,
+ // and don't hide anything which was initially visible.
+ testCollapsed("4", -1, 1000, 500);
+
+ // Test that scrolling a translated element into view takes
+ // account of the transform.
+ testCollapsed("5", 0, 0, 400);
+
+ // Test that scrolling a scaled element into view takes
+ // account of the transform.
+ testCollapsed("6", 0, 0, 150);
+
+ // Test that scrolling an element with a translated, scrolling container
+ // into view takes account of the transform.
+ testCollapsed("7", 0, 0, 400);
+
+ win.close();
+ SimpleTest.finish();
+}
+
+function openWindow() {
+ win = open("scroll_selection_into_view_window.html", "_blank", "width=500,height=350");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(openWindow);
+</script>
+</pre>
+</body>
+
+</html>
diff --git a/layout/base/tests/test_scroll_space_no_range_overflow_scroll.html b/layout/base/tests/test_scroll_space_no_range_overflow_scroll.html
new file mode 100644
index 0000000000..deed8f4ced
--- /dev/null
+++ b/layout/base/tests/test_scroll_space_no_range_overflow_scroll.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<title>Test for bug 1567237</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script type="text/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<style>
+ .spacer { height: 200vh; }
+ .scroller { height: 300px; overflow: scroll; }
+</style>
+<div id="unscrollable" class="scroller" tabindex=0></div>
+<div id="scrollable" class="scroller" tabindex=0>
+ <div class="spacer"></div>
+</div>
+<div class="spacer"></div>
+<script>
+function waitForScrollEvent(target) {
+ return new Promise(resolve => {
+ target.addEventListener("scroll", resolve, { once: true });
+ });
+}
+
+let selectionController =
+ SpecialPowers.wrap(window)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+ .getInterface(SpecialPowers.Ci.nsISelectionDisplay)
+ .QueryInterface(SpecialPowers.Ci.nsISelectionController);
+
+function doPageDown(targetExpectedToScroll) {
+ let promise = waitForScrollEvent(targetExpectedToScroll);
+ selectionController.pageMove(true, false);
+ return promise;
+}
+
+promise_test(async function() {
+ await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]});
+
+ const rootScroller = document.documentElement;
+ const scrollable = document.querySelector("#scrollable");
+ const unscrollable = document.querySelector("#unscrollable");
+
+ assert_equals(rootScroller.scrollTop, 0, "Root should start unscrolled");
+ assert_equals(scrollable.scrollTop, 0, "#scrollable should start unscrolled");
+ assert_equals(unscrollable.scrollTop, 0, "#unscrollable should not be able to scroll at all");
+
+ assert_true(rootScroller.scrollTopMax > 0, "Should be able to scroll the document element");
+ assert_true(scrollable.scrollTopMax > 0, "Should be able to scroll #scrollable");
+ assert_equals(unscrollable.scrollTopMax, 0, "#unscrollable should not be able to scroll at all (checking scrollTopMax)");
+
+ scrollable.focus();
+ await waitToClearOutAnyPotentialScrolls(window);
+ await doPageDown(scrollable);
+ assert_not_equals(scrollable.scrollTop, 0, "Should have scrolled when pressing space");
+
+ unscrollable.focus();
+ await waitToClearOutAnyPotentialScrolls(window);
+ let rootScrollTop = rootScroller.scrollTop; // Could've scrolled to scroll `scrollable` into view before.
+ await doPageDown(window);
+ assert_equals(unscrollable.scrollTop, 0, "Should not be able to scroll the unscrollable div");
+ assert_not_equals(rootScroller.scrollTop, rootScrollTop, "Root should be able to scroll");
+
+ // Null out the controller. Otherwise we leak the whole window because
+ // PresShell is not cycle-collectable. See bug 1567237.
+ selectionController = null;
+}, "Overflow scroll without range doesn't block scrolling of the main document");
+</script>
diff --git a/layout/base/tests/test_synthmousemove.html b/layout/base/tests/test_synthmousemove.html
new file mode 100644
index 0000000000..fda86c341e
--- /dev/null
+++ b/layout/base/tests/test_synthmousemove.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<title>test synth mouse moves go to the right place with fission</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ window.open("file_synthmousemove.html", "_blank");
+}
+
+function reportSuccess() {
+ ok(true, "reportSuccess");
+ SimpleTest.finish();
+}
+
+var smoothScrollPref = "general.smoothScroll";
+function prepareTest() {
+ if (!SpecialPowers.getBoolPref("layout.reflow.synthMouseMove")) {
+ ok(true, "layout.reflow.synthMouseMove is false, we can't run this test");
+ SimpleTest.finish();
+ return;
+ }
+ window.requestAnimationFrame(function() {
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, runTest);
+ });
+}
+
+SimpleTest.waitForFocus(prepareTest);
+</script>
diff --git a/layout/base/tests/test_transformed_scrolling_repaints.html b/layout/base/tests/test_transformed_scrolling_repaints.html
new file mode 100644
index 0000000000..81cf9d7ac2
--- /dev/null
+++ b/layout/base/tests/test_transformed_scrolling_repaints.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that scaled elements with scrolled contents don't repaint unnecessarily when we scroll inside them</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="setPrefAndStartTest()">
+<div id="t" style="-moz-transform: scale(1.2, 1.2); -moz-transform-origin:top left; width:200px; height:500px; background:yellow; overflow:auto">
+ <div style="height:40px;">Hello</div>
+ <div id="e" style="height:30px; background:lime">Kitty</div>
+ <div style="height:800px; background:yellow">Kitty</div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var t = document.getElementById("t");
+var e = document.getElementById("e");
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+function startTest() {
+ // Do a couple of scrolls to ensure we've triggered activity heuristics.
+ waitForAllPaintsFlushed(function () {
+ t.scrollTop = 5;
+ waitForAllPaintsFlushed(function () {
+ t.scrollTop = 10;
+ waitForAllPaintsFlushed(function () {
+ // Clear paint state now and scroll again.
+ utils.checkAndClearPaintedState(e);
+ t.scrollTop = 15;
+ waitForAllPaintsFlushed(function () {
+ var painted = utils.checkAndClearPaintedState(e);
+ is(painted, false, "Fully-visible scrolled element should not have been painted");
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+function setPrefAndStartTest() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [["layers.single-tile.enabled", false]]},
+ // Need a timeout here to allow paint unsuppression before we start the test
+ function() {
+ setTimeout(startTest, 0);
+ }
+ );
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_transformed_scrolling_repaints_2.html b/layout/base/tests/test_transformed_scrolling_repaints_2.html
new file mode 100644
index 0000000000..4611f0c25a
--- /dev/null
+++ b/layout/base/tests/test_transformed_scrolling_repaints_2.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that scaled elements with scrolled contents don't repaint unnecessarily when we scroll inside them (1.1 scale)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="setPrefAndStartTest()">
+<div id="t" style="-moz-transform: scale(1.1, 1.1); -moz-transform-origin:top left; width:200px; height:100px; background:yellow; overflow:hidden">
+ <div style="height:40px;"></div>
+ <div id="e" style="height:30px; background:lime"></div>
+ <div style="height:300px; background:yellow"></div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var t = document.getElementById("t");
+var e = document.getElementById("e");
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+function startTest() {
+ // Do a couple of scrolls to ensure we've triggered activity heuristics
+ waitForAllPaintsFlushed(function () {
+ t.scrollTop = 5;
+ waitForAllPaintsFlushed(function () {
+ t.scrollTop = 10;
+ waitForAllPaintsFlushed(function () {
+ // Clear paint state now and scroll again.
+ utils.checkAndClearPaintedState(e);
+ t.scrollTop = 20;
+ waitForAllPaintsFlushed(function () {
+ var painted = utils.checkAndClearPaintedState(e);
+ is(painted, false, "Fully-visible scrolled element should not have been painted");
+ SimpleTest.finish();
+ });
+ });
+ });
+ });
+}
+function setPrefAndStartTest() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [["layers.single-tile.enabled", false]]},
+ // Need a timeout here to allow paint unsuppression before we start the test
+ function() {
+ setTimeout(startTest, 0);
+ }
+ );
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_transformed_scrolling_repaints_3.html b/layout/base/tests/test_transformed_scrolling_repaints_3.html
new file mode 100644
index 0000000000..eb9ad9ba93
--- /dev/null
+++ b/layout/base/tests/test_transformed_scrolling_repaints_3.html
@@ -0,0 +1,24 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test that scaled elements with scrolled contents don't repaint unnecessarily when we scroll inside them</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<!-- Need a timeout here to allow paint unsuppression before we start the test -->
+<body>
+<pre id="test">
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv(
+ {"set": [["layers.single-tile.enabled", false]]},
+ function() {
+ window.open("transformed_scrolling_repaints_3_window.html", "transformed_scrolling_repaints_3",
+ "chrome,width=350,height=450");
+ }
+);
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/test_visual_viewport_in_oopif.html b/layout/base/tests/test_visual_viewport_in_oopif.html
new file mode 100644
index 0000000000..9056336471
--- /dev/null
+++ b/layout/base/tests/test_visual_viewport_in_oopif.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+<style>
+#iframe {
+ width: 300px;
+ height: 300px;
+ overflow: scroll;
+}
+</style>
+<iframe id="iframe"></iframe>
diff --git a/layout/base/tests/test_zoom_restore_bfcache.html b/layout/base/tests/test_zoom_restore_bfcache.html
new file mode 100644
index 0000000000..798ce1c6e4
--- /dev/null
+++ b/layout/base/tests/test_zoom_restore_bfcache.html
@@ -0,0 +1,139 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test for zoom restoration when coming from the bfcache</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+/**
+ * - main page (this one) opens file_zoom_restore_bfcache.html
+ * - file_zoom_restore_bfcache.html sends "handlePageShow" to main page
+ * - main page sends file_zoom_restore_bfcache.html "case1sendData"
+ * - file_zoom_restore_bfcache.html sends "case1data" to main page
+ * - main page sends "case1click" to file_zoom_restore_bfcache.html
+ * - file_zoom_restore_bfcache.html clicks on <a> element, navigating to uri
+ * file_zoom_restore_bfcache.html?2, and gets bfcached
+ * - file_zoom_restore_bfcache.html?2 sends "handlePageShow" to main page
+ * - main page sends "case2sendData" to file_zoom_restore_bfcache.html?2
+ * - file_zoom_restore_bfcache.html?2 sends "case2data" to main page
+ * - main page sends "case2action" to file_zoom_restore_bfcache.html?2
+ * - file_zoom_restore_bfcache.html?2 sends "case2dataAnimationFrame" to main page
+ * - main page sends "case2back" to file_zoom_restore_bfcache.html?2
+ * - file_zoom_restore_bfcache.html?2 navigates back to file_zoom_restore_bfcache.html
+ * - file_zoom_restore_bfcache.html sends "handlePageShow" to main page
+ * - main page sends "case3sendData to file_zoom_restore_bfcache.html
+ * - file_zoom_restore_bfcache.html sends "case3data" to main page
+ * - main page sends "close to file_zoom_restore_bfcache.html
+ * - file_zoom_restore_bfcache.html closes bc and window and sends back "closed"
+ **/
+
+const originalDPR = window.devicePixelRatio;
+let loadCount = 0;
+var bc = new BroadcastChannel("zoomRestoreBfcache");
+var bcPage2 = new BroadcastChannel("zoomRestoreBfcache?2");
+bc.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ info(`Main page, received command from normal bc=${command}`);
+ switch (command) {
+ case "handlePageShow": {
+ handlePageShow(msgEvent.data.eventPersisted);
+ break;
+ }
+ case "case1data": {
+ is(loadCount, 1, "Case 1");
+ is(msg.devicePixelRatio, originalDPR, "No zoom");
+ bc.postMessage({command: "case1click"});
+ // The end of case 1
+ break;
+ }
+ case "case3data": {
+ is(loadCount, 2, "Case 3");
+ is(msg.devicePixelRatio, originalDPR * 2, "Should preserve zoom when restored");
+ SpecialPowers.spawnChrome([], () => {
+ // We use FullZoom in file_zoom_restore_bfcache.html to set the zoom level in the
+ // parent, but if FullZoom is not available then that will fail. So check it here
+ // and mark the test as todo if it's not available.
+ return "FullZoom" in this.browsingContext.top.embedderElement.ownerGlobal;
+ }).then((hasFullZoom) => {
+ if (SpecialPowers.Services.appinfo.sessionHistoryInParent && hasFullZoom) {
+ // When bfcache lives in the parent process, we get a proper zoom level
+ // update on the browsing context.
+ is(msg.frameDevicePixelRatio, originalDPR * 2, "Should preserve zoom on frames too");
+ } else {
+ todo_is(msg.frameDevicePixelRatio, originalDPR * 2, "Should preserve zoom on frames too");
+ }
+ bc.postMessage({command: "close"});
+ // Now we wait for "closed"
+ });
+ break;
+ }
+ case "closed": {
+ is(loadCount, 2, "Case 3");
+ bc.close();
+ SimpleTest.finish();
+ break;
+ }
+ default:
+ ok(false, "should not receive extra messages via BroadcastChannel");
+ }
+}
+bcPage2.onmessage = (msgEvent) => {
+ var msg = msgEvent.data;
+ var command = msg.command;
+ info(`Main page, received command from bc?2=${command}`);
+ switch (command) {
+ case "handlePageShow": {
+ handlePageShow(msgEvent.data.eventPersisted);
+ break;
+ }
+ case "case2data": {
+ is(loadCount, 2, "Case 2");
+ is(msg.devicePixelRatio, originalDPR, "No zoom (yet)")
+ is(msg.frameDevicePixelRatio, originalDPR, "No zoom on frame either");
+ bcPage2.postMessage({command: "case2action"});
+ // Now we wait for "case2dataAnimationFrame"
+ break;
+ }
+ case "case2dataAnimationFrame": {
+ is(loadCount, 2, "Case 2");
+ is(msg.devicePixelRatio, originalDPR * 2, "Zoomed");
+ is(msg.frameDevicePixelRatio, originalDPR * 2, "Zoomed iframe too");
+ bcPage2.postMessage({command: "case2back"});
+ bcPage2.close();
+ // The end of case 2
+ break;
+ }
+ default:
+ ok(false, "should not receive extra messages via BroadcastChannel");
+ }
+}
+function handlePageShow(persisted) {
+ ok(typeof persisted == "boolean", "Should get the persisted state from the pageshow event");
+ if (loadCount == 2) {
+ ok(persisted, "Should've gone into the bfcache after the back navigation");
+ } else {
+ ok(!persisted, "Should NOT be retrieved from bfcache");
+ }
+
+ if (loadCount == 0) {
+ loadCount++;
+ bc.postMessage({command: "case1sendData"});
+ // Now we wait for the "case1data" message
+ } else if (loadCount == 1) {
+ loadCount++;
+ bcPage2.postMessage({command: "case2sendData"});
+ // Now we wait for the "case2data" message
+ } else {
+ bc.postMessage({command: "case3sendData"});
+ // Now we wait for the "case3data" message
+ }
+}
+
+// If Fission is disabled, the pref is no-op.
+SpecialPowers.pushPrefEnv({set: [["fission.bfcacheInParent", true]]}, () => {
+ window.open('file_zoom_restore_bfcache.html', '_blank', 'noopener');
+});
+</script>
diff --git a/layout/base/tests/textarea-invalid-ref.html b/layout/base/tests/textarea-invalid-ref.html
new file mode 100644
index 0000000000..c5607603d7
--- /dev/null
+++ b/layout/base/tests/textarea-invalid-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <textarea style="background-color:red">foo</textarea>
+ </body>
+</html>
+
diff --git a/layout/base/tests/textarea-maxlength-invalid-change.html b/layout/base/tests/textarea-maxlength-invalid-change.html
new file mode 100644
index 0000000000..29571678b0
--- /dev/null
+++ b/layout/base/tests/textarea-maxlength-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with maxlength is invalid if the user edits and it's too long -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ synthesizeKey("KEY_Backspace");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" maxlength="2">fooo</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-maxlength-ui-invalid-change.html b/layout/base/tests/textarea-maxlength-ui-invalid-change.html
new file mode 100644
index 0000000000..9c674080ac
--- /dev/null
+++ b/layout/base/tests/textarea-maxlength-ui-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with maxlength is -moz-ui-invalid if the user edits and it's too long -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ synthesizeKey("KEY_Backspace");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" maxlength="2">fooo</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-maxlength-ui-valid-change.html b/layout/base/tests/textarea-maxlength-ui-valid-change.html
new file mode 100644
index 0000000000..2f3d6bda1b
--- /dev/null
+++ b/layout/base/tests/textarea-maxlength-ui-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with maxlength is -moz-ui-valid if the user edits and it's not too long -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ synthesizeKey("KEY_Backspace"); // so that it becomes invalid first
+ textarea.blur();
+ textarea.focus();
+ synthesizeKey("KEY_Backspace");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" maxlength="3">foooo</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-maxlength-valid-before-change.html b/layout/base/tests/textarea-maxlength-valid-before-change.html
new file mode 100644
index 0000000000..3466d310a4
--- /dev/null
+++ b/layout/base/tests/textarea-maxlength-valid-before-change.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with maxlength is valid until the user edits it, even if it's too long -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ </head>
+ <body onload="document.documentElement.className=''">
+ <textarea id="textarea" maxlength="2">foo</textarea>
+ </body>
+</html>
+
diff --git a/layout/base/tests/textarea-maxlength-valid-change.html b/layout/base/tests/textarea-maxlength-valid-change.html
new file mode 100644
index 0000000000..24007a500f
--- /dev/null
+++ b/layout/base/tests/textarea-maxlength-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with maxlength is valid if the user edits and it's not too long -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ synthesizeKey("KEY_Backspace"); // so that it becomes invalid first
+ textarea.blur();
+ textarea.focus();
+ synthesizeKey("KEY_Backspace");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" maxlength="3">foooo</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-minlength-invalid-change.html b/layout/base/tests/textarea-minlength-invalid-change.html
new file mode 100644
index 0000000000..9267ccfcaf
--- /dev/null
+++ b/layout/base/tests/textarea-minlength-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with minlength is invalid if the user edits and it's too short -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ sendString("o");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" minlength="4">fo</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-minlength-ui-invalid-change.html b/layout/base/tests/textarea-minlength-ui-invalid-change.html
new file mode 100644
index 0000000000..e52fba71e2
--- /dev/null
+++ b/layout/base/tests/textarea-minlength-ui-invalid-change.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with minlength is -moz-ui-invalid if the user edits and it's too short -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { box-shadow:none; background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ sendString("o");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" minlength="4">fo</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-minlength-ui-valid-change.html b/layout/base/tests/textarea-minlength-ui-valid-change.html
new file mode 100644
index 0000000000..18cecef138
--- /dev/null
+++ b/layout/base/tests/textarea-minlength-ui-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with minlength is -moz-ui-valid if the user edits and it's not too short -->
+ <head>
+ <style>
+ :-moz-ui-valid { background-color:green; }
+ :-moz-ui-invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ sendString("o"); // so that it becomes invalid first
+ textarea.blur();
+ textarea.focus();
+ sendString("o");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" minlength="3">f</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-minlength-valid-before-change.html b/layout/base/tests/textarea-minlength-valid-before-change.html
new file mode 100644
index 0000000000..6fd7ad9799
--- /dev/null
+++ b/layout/base/tests/textarea-minlength-valid-before-change.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with minlength is valid until the user edits it, even if it's too short -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ </head>
+ <body onload="document.documentElement.className=''">
+ <textarea id="textarea" minlength="5">foo</textarea>
+ </body>
+</html>
+
diff --git a/layout/base/tests/textarea-minlength-valid-change.html b/layout/base/tests/textarea-minlength-valid-change.html
new file mode 100644
index 0000000000..95faa54763
--- /dev/null
+++ b/layout/base/tests/textarea-minlength-valid-change.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+ <!-- Test: textarea with minlength is valid if the user edits and it's not too short -->
+ <head>
+ <style>
+ :valid { background-color:green; }
+ :invalid { background-color:red; }
+ * { background-color:white; }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ function runTest() {
+ var textarea = document.getElementById('textarea');
+ textarea.setSelectionRange(textarea.value.length, textarea.value.length)
+ textarea.focus();
+ sendString("o"); // so that it becomes invalid first
+ textarea.blur();
+ textarea.focus();
+ sendString("o");
+ textarea.blur(); // to hide the caret
+ document.documentElement.className='';
+ }
+ </script>
+ </head>
+ <body onload="runTest()">
+ <textarea id="textarea" minlength="3">f</textarea>
+ </body>
+</html>
diff --git a/layout/base/tests/textarea-valid-ref.html b/layout/base/tests/textarea-valid-ref.html
new file mode 100644
index 0000000000..547b4fb7c5
--- /dev/null
+++ b/layout/base/tests/textarea-valid-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+ <body>
+ <textarea style="background-color:green">foo</textarea>
+ </body>
+</html>
+
diff --git a/layout/base/tests/transformed_scrolling_repaints_3_window.html b/layout/base/tests/transformed_scrolling_repaints_3_window.html
new file mode 100644
index 0000000000..ae6a05294d
--- /dev/null
+++ b/layout/base/tests/transformed_scrolling_repaints_3_window.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html style="overflow: hidden;">
+<head>
+ <title>Test that scaled elements with scrolled contents don't repaint unnecessarily when we scroll inside them</title>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+</head>
+<!-- Need a timeout here to allow paint unsuppression before we start the test -->
+<body onload="setTimeout(startTest,0)" style="background:white;">
+<iframe id="t" style="-moz-transform: scale(0.48979); -moz-transform-origin:top left; width:500px; height:600px;"
+ src="transformed_scrolling_repaints_3_window_frame.html">
+</iframe>
+<pre id="test">
+<script type="application/javascript">
+var SimpleTest = window.opener.SimpleTest;
+var SpecialPowers = window.opener.SpecialPowers;
+var is = window.opener.is;
+var smoothScrollPref = "general.smoothScroll";
+
+function startTest() {
+ SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, runTest);
+}
+
+async function runTest() {
+ let t = document.getElementById("t");
+ let e = t.contentDocument.getElementById("e");
+ t.contentWindow.scrollTo(0,0);
+ let utils = SpecialPowers.getDOMWindowUtils(window);
+
+ for (let i = 0; i < 15; i++) {
+ let painted = utils.checkAndClearPaintedState(e);
+ // We ignore the first few scrolls, to ensure we have triggered activity
+ // heuristics.
+ if (i >= 5) {
+ is(painted, false,
+ "Fully-visible scrolled element should not have been painted");
+ }
+ t.contentWindow.scrollByLines(1);
+ await promiseAllPaintsDone(null, true);
+ }
+ SimpleTest.finish();
+ window.close();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/layout/base/tests/transformed_scrolling_repaints_3_window_frame.html b/layout/base/tests/transformed_scrolling_repaints_3_window_frame.html
new file mode 100644
index 0000000000..a3213908d6
--- /dev/null
+++ b/layout/base/tests/transformed_scrolling_repaints_3_window_frame.html
@@ -0,0 +1,58 @@
+<body style='background:yellow;'>
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p id='e'>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+<p>My graduate adviser was the most patient, understanding, and helpful
+person I've ever had the joy of dealing with. That doesn't change that
+there are some real dicks out there, and some of them happen to be
+scientists.
+</body>
diff --git a/layout/base/tests/visual_viewport_in_child.html b/layout/base/tests/visual_viewport_in_child.html
new file mode 100644
index 0000000000..3fd47cb6bd
--- /dev/null
+++ b/layout/base/tests/visual_viewport_in_child.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width, minimum-scale=0.25">
+<style>
+html {
+ overflow: hidden;
+}
+html, body {
+ margin: 0;
+ width: 100%;
+ height: 100%;
+}
+div {
+ position: absolute;
+}
+</style>
+<div style="background: red; width: 300%; height: 300%;"></div>
+<div style="background: green; width: 200%; height: 200%;"></div>
+<div style="background: blue; width: 100%; height: 100%;"></div>
+<script>
+window.onload = () => {
+ parent.postMessage({ width: window.visualViewport.width,
+ height: window.visualViewport.height } , "*");
+};
+</script>