summaryrefslogtreecommitdiffstats
path: root/dom/events/test
diff options
context:
space:
mode:
Diffstat (limited to 'dom/events/test')
-rw-r--r--dom/events/test/browser.toml33
-rw-r--r--dom/events/test/browser_alt_keyup_in_content.js333
-rw-r--r--dom/events/test/browser_beforeinput_by_execCommand_in_contentscript.js195
-rw-r--r--dom/events/test/browser_bug1539497.js25
-rw-r--r--dom/events/test/browser_dragimage.js65
-rw-r--r--dom/events/test/browser_keyboard_event_init_key_event_enabled_in_contentscript.js80
-rw-r--r--dom/events/test/browser_mouse_enterleave_switch_tab.js158
-rw-r--r--dom/events/test/browser_mouseout_notification_panel.js145
-rw-r--r--dom/events/test/browser_shortcutkey_modifier_conflicts_with_content_accesskey_modifier.js102
-rw-r--r--dom/events/test/bug1017086_inner.html39
-rw-r--r--dom/events/test/bug226361_iframe.xhtml47
-rw-r--r--dom/events/test/bug299673.js150
-rw-r--r--dom/events/test/bug322588-popup.html1
-rw-r--r--dom/events/test/bug415498-doc1.html15
-rw-r--r--dom/events/test/bug415498-doc2.html15
-rw-r--r--dom/events/test/bug418986-3.js85
-rw-r--r--dom/events/test/bug426082.html116
-rw-r--r--dom/events/test/bug545268.html1
-rw-r--r--dom/events/test/bug574663.html3
-rw-r--r--dom/events/test/bug591249_iframe.xhtml33
-rw-r--r--dom/events/test/bug602962.xhtml8
-rw-r--r--dom/events/test/bug607464.html3
-rw-r--r--dom/events/test/bug656379-1.html186
-rw-r--r--dom/events/test/chrome.toml48
-rw-r--r--dom/events/test/clipboard/browser.toml40
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_clickjacking.js69
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js264
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression_ext.js156
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_read.js228
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_readText.js241
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_readText_multiple.js316
-rw-r--r--dom/events/test/clipboard/browser_navigator_clipboard_touch.js114
-rw-r--r--dom/events/test/clipboard/chrome.toml3
-rw-r--r--dom/events/test/clipboard/file_iframe.html2
-rw-r--r--dom/events/test/clipboard/file_toplevel.html10
-rw-r--r--dom/events/test/clipboard/head.js185
-rw-r--r--dom/events/test/clipboard/mochitest.toml7
-rw-r--r--dom/events/test/clipboard/simple_navigator_clipboard_keydown.html15
-rw-r--r--dom/events/test/clipboard/simple_navigator_clipboard_read.html64
-rw-r--r--dom/events/test/clipboard/simple_navigator_clipboard_readText.html47
-rw-r--r--dom/events/test/clipboard/test_async_clipboard.xhtml124
-rw-r--r--dom/events/test/clipboard/test_paste_image.html213
-rw-r--r--dom/events/test/dragimage.html10
-rw-r--r--dom/events/test/empty.js0
-rw-r--r--dom/events/test/error_event_worker.js19
-rw-r--r--dom/events/test/event_leak_utils.js84
-rw-r--r--dom/events/test/file_beforeinput_by_execCommand_in_contentscript.html9
-rw-r--r--dom/events/test/file_bug1446834.html96
-rw-r--r--dom/events/test/file_bug1484371.html94
-rw-r--r--dom/events/test/file_bug1692052.html25
-rw-r--r--dom/events/test/file_bug679494.html8
-rw-r--r--dom/events/test/file_coalesce_touchmove_browserchild.html117
-rw-r--r--dom/events/test/file_coalesce_touchmove_browserchild2.html193
-rw-r--r--dom/events/test/file_coalesce_touchmove_ipc.html193
-rw-r--r--dom/events/test/file_empty.html15
-rw-r--r--dom/events/test/file_event_screenXY.html23
-rw-r--r--dom/events/test/file_focus_blur_on_click_in_cross_origin_iframe.html28
-rw-r--r--dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_inner.html28
-rw-r--r--dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_middle.html37
-rw-r--r--dom/events/test/file_keyboard_event_init_key_event_enabled_in_contentscript.html21
-rw-r--r--dom/events/test/file_mouse_enterleave.html45
-rw-r--r--dom/events/test/green.pngbin0 -> 255 bytes
-rw-r--r--dom/events/test/gtest/TestShortcutKeyDefinitions.cpp634
-rw-r--r--dom/events/test/gtest/moz.build18
-rw-r--r--dom/events/test/mochitest.toml523
-rw-r--r--dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html63
-rw-r--r--dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html64
-rw-r--r--dom/events/test/pointerevents/bug968148_inner.html316
-rw-r--r--dom/events/test/pointerevents/bug968148_inner2.html315
-rw-r--r--dom/events/test/pointerevents/bug_1420589_iframe1.html17
-rw-r--r--dom/events/test/pointerevents/bug_1420589_iframe2.html17
-rw-r--r--dom/events/test/pointerevents/chrome.toml3
-rw-r--r--dom/events/test/pointerevents/file_pointercapture_xorigin_iframe.html62
-rw-r--r--dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_pointerlock.html111
-rw-r--r--dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_touch.html69
-rw-r--r--dom/events/test/pointerevents/file_test_trigger_fullscreen.html1
-rw-r--r--dom/events/test/pointerevents/iframe.html7
-rw-r--r--dom/events/test/pointerevents/mochitest.toml250
-rw-r--r--dom/events/test/pointerevents/mochitest_support_external.js270
-rw-r--r--dom/events/test/pointerevents/mochitest_support_internal.js125
-rw-r--r--dom/events/test/pointerevents/pointerevent_utils.js60
-rw-r--r--dom/events/test/pointerevents/readme.md9
-rw-r--r--dom/events/test/pointerevents/test_bug1285128.html51
-rw-r--r--dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html30
-rw-r--r--dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html30
-rw-r--r--dom/events/test/pointerevents/test_bug1303704.html135
-rw-r--r--dom/events/test/pointerevents/test_bug1315862.html66
-rw-r--r--dom/events/test/pointerevents/test_bug1323158.html91
-rw-r--r--dom/events/test/pointerevents/test_bug1403055.html90
-rw-r--r--dom/events/test/pointerevents/test_bug1420589_1.html105
-rw-r--r--dom/events/test/pointerevents/test_bug1420589_2.html122
-rw-r--r--dom/events/test/pointerevents/test_bug1420589_3.html115
-rw-r--r--dom/events/test/pointerevents/test_bug1697769.xhtml74
-rw-r--r--dom/events/test/pointerevents/test_bug1725416.html37
-rw-r--r--dom/events/test/pointerevents/test_bug968148.html46
-rw-r--r--dom/events/test/pointerevents/test_getCoalescedEvents.html88
-rw-r--r--dom/events/test/pointerevents/test_getCoalescedEvents_touch.html111
-rw-r--r--dom/events/test/pointerevents/test_multiple_touches.html196
-rw-r--r--dom/events/test/pointerevents/test_pointercapture_remove_iframe.html80
-rw-r--r--dom/events/test/pointerevents/test_pointercapture_xorigin_iframe.html55
-rw-r--r--dom/events/test/pointerevents/test_pointermove_drag_scrollbar.html77
-rw-r--r--dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html166
-rw-r--r--dom/events/test/pointerevents/test_synthesized_touch.html47
-rw-r--r--dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html54
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_attributes_hoverable_pointers-manual.html53
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_attributes_nohover_pointers-manual.html26
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_boundary_events_in_capturing-manual.html46
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html39
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html26
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_drag_interaction-manual.html38
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_movementxy-manual.html53
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_multiple_primary_pointers_boundary_events-manual.html31
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html27
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_pointercancel_touch-manual.html30
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_after_pointercancel_touch-manual.html30
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_pen-manual.html28
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_after_pointercancel_touch-manual.html30
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_pen-manual.html28
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_events_to_original_target-manual.html49
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_onpointercancel_touch-manual.html30
-rw-r--r--dom/events/test/pointerevents/test_wpt_pointerevent_sequence_at_implicit_release_on_drag-manual.html27
-rw-r--r--dom/events/test/pointerevents/test_wpt_touch_action.html98
-rw-r--r--dom/events/test/pointerevents/touch_action_helpers.js243
-rw-r--r--dom/events/test/pointerevents/wpt/compat/pointerevent_touch-action_two-finger_interaction-manual.html102
-rw-r--r--dom/events/test/pointerevents/wpt/html/pointerevent_drag_interaction-manual.html103
-rw-r--r--dom/events/test/pointerevents/wpt/idlharness.html104
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_attributes_hoverable_pointers-manual.html143
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_attributes_nohover_pointers-manual.html126
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_boundary_events_in_capturing-manual.html97
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html135
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_constructor.html106
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html145
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html82
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_pointercancel_touch-manual.html77
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_pointerleave_after_pointercancel_touch-manual.html66
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_pointerleave_pen-manual.html58
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_pointerout_after_pointercancel_touch-manual.html67
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_pointerout_pen-manual.html57
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html137
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html71
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_click-manual.html83
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_drag-manual.html84
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_styles.css112
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_support.js333
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-auto-css_touch-manual.html129
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-button-test_touch-manual.html110
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-illegal.html67
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html117
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-none_touch-manual.html112
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html112
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html117
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html133
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_parent-none_touch-manual.html112
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-keyboard-manual.html124
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-mouse-manual.html130
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-none-css_touch-manual.html111
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-down-css_touch-manual.html114
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-left-css_touch-manual.html114
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-right-css_touch-manual.html114
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-up-css_touch-manual.html114
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-css_touch-manual.html106
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html111
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y_touch-manual.html126
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-y-css_touch-manual.html106
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-span-test_touch-manual.html114
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-svg-test_touch-manual.html122
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-table-test_touch-manual.html145
-rw-r--r--dom/events/test/pointerevents/wpt/pointerevent_touch-action-verification.html91
-rw-r--r--dom/events/test/pointerevents/wpt/pointerlock/pointerevent_movementxy-manual.html99
-rw-r--r--dom/events/test/pointerevents/wpt/pointerlock/resources/pointerevent_movementxy-iframe.html8
-rw-r--r--dom/events/test/pointerevents/wpt/resources/pointerevent_attributes_hoverable_pointers-iframe.html10
-rw-r--r--dom/events/test/pointerevents/wpt/resources/pointerevent_pointerId_scope-iframe.html35
-rw-r--r--dom/events/test/test_DataTransferItemList.html233
-rw-r--r--dom/events/test/test_accel_virtual_modifier.html90
-rw-r--r--dom/events/test/test_accesskey.html160
-rw-r--r--dom/events/test/test_addEventListenerExtraArg.html31
-rw-r--r--dom/events/test/test_all_synthetic_events.html488
-rw-r--r--dom/events/test/test_auxclick_autoscroll_off.html31
-rw-r--r--dom/events/test/test_bug1003432.html45
-rw-r--r--dom/events/test/test_bug1003432.js31
-rw-r--r--dom/events/test/test_bug1013412.html116
-rw-r--r--dom/events/test/test_bug1017086_enable.html35
-rw-r--r--dom/events/test/test_bug1037990.html61
-rw-r--r--dom/events/test/test_bug1079236.html66
-rw-r--r--dom/events/test/test_bug1127588.html62
-rw-r--r--dom/events/test/test_bug1128787-1.html52
-rw-r--r--dom/events/test/test_bug1128787-2.html54
-rw-r--r--dom/events/test/test_bug1128787-3.html53
-rw-r--r--dom/events/test/test_bug1145910.html54
-rw-r--r--dom/events/test/test_bug1150308.html49
-rw-r--r--dom/events/test/test_bug1248459.html58
-rw-r--r--dom/events/test/test_bug1264380.html82
-rw-r--r--dom/events/test/test_bug1298970.html33
-rw-r--r--dom/events/test/test_bug1304044.html133
-rw-r--r--dom/events/test/test_bug1305458.html50
-rw-r--r--dom/events/test/test_bug1327798.html47
-rw-r--r--dom/events/test/test_bug1332699.html37
-rw-r--r--dom/events/test/test_bug1339758.html80
-rw-r--r--dom/events/test/test_bug1369072.html37
-rw-r--r--dom/events/test/test_bug1412775.xhtml66
-rw-r--r--dom/events/test/test_bug1429572.html43
-rw-r--r--dom/events/test/test_bug1446834.html33
-rw-r--r--dom/events/test/test_bug1447993.html41
-rw-r--r--dom/events/test/test_bug1484371.html27
-rw-r--r--dom/events/test/test_bug1534562.html51
-rw-r--r--dom/events/test/test_bug1539497.html29
-rw-r--r--dom/events/test/test_bug1581192.html73
-rw-r--r--dom/events/test/test_bug1635018.html27
-rw-r--r--dom/events/test/test_bug1637259.html71
-rw-r--r--dom/events/test/test_bug1673434.html75
-rw-r--r--dom/events/test/test_bug1681800.html26
-rw-r--r--dom/events/test/test_bug1686716.html28
-rw-r--r--dom/events/test/test_bug1692052.html36
-rw-r--r--dom/events/test/test_bug1692277.html43
-rw-r--r--dom/events/test/test_bug1709832.html46
-rw-r--r--dom/events/test/test_bug1710509.html49
-rw-r--r--dom/events/test/test_bug1728171.html70
-rw-r--r--dom/events/test/test_bug226361.xhtml82
-rw-r--r--dom/events/test/test_bug238987.html280
-rw-r--r--dom/events/test/test_bug288392.html103
-rw-r--r--dom/events/test/test_bug299673-1.html61
-rw-r--r--dom/events/test/test_bug299673-2.html60
-rw-r--r--dom/events/test/test_bug322588.html66
-rw-r--r--dom/events/test/test_bug328885.html135
-rw-r--r--dom/events/test/test_bug336682.js96
-rw-r--r--dom/events/test/test_bug336682_1.html55
-rw-r--r--dom/events/test/test_bug336682_2.xhtml58
-rw-r--r--dom/events/test/test_bug367781.html53
-rw-r--r--dom/events/test/test_bug379120.html55
-rw-r--r--dom/events/test/test_bug402089.html67
-rw-r--r--dom/events/test/test_bug405632.html34
-rw-r--r--dom/events/test/test_bug409604.html379
-rw-r--r--dom/events/test/test_bug412567.html47
-rw-r--r--dom/events/test/test_bug415498.xhtml97
-rw-r--r--dom/events/test/test_bug418986-3.html25
-rw-r--r--dom/events/test/test_bug418986-3.xhtml27
-rw-r--r--dom/events/test/test_bug422132.html125
-rw-r--r--dom/events/test/test_bug426082.html30
-rw-r--r--dom/events/test/test_bug427537.html61
-rw-r--r--dom/events/test/test_bug428988.html44
-rw-r--r--dom/events/test/test_bug432698.html223
-rw-r--r--dom/events/test/test_bug443985.html76
-rw-r--r--dom/events/test/test_bug447736.html48
-rw-r--r--dom/events/test/test_bug448602.html303
-rw-r--r--dom/events/test/test_bug450876.html47
-rw-r--r--dom/events/test/test_bug456273.html46
-rw-r--r--dom/events/test/test_bug457672.html55
-rw-r--r--dom/events/test/test_bug489671.html56
-rw-r--r--dom/events/test/test_bug493251.html181
-rw-r--r--dom/events/test/test_bug508479.html110
-rw-r--r--dom/events/test/test_bug517851.html122
-rw-r--r--dom/events/test/test_bug524674.xhtml130
-rw-r--r--dom/events/test/test_bug534833.html156
-rw-r--r--dom/events/test/test_bug545268.html129
-rw-r--r--dom/events/test/test_bug547996-1.html87
-rw-r--r--dom/events/test/test_bug547996-2.xhtml120
-rw-r--r--dom/events/test/test_bug547996-3.xhtml66
-rw-r--r--dom/events/test/test_bug556493.html74
-rw-r--r--dom/events/test/test_bug563329.html82
-rw-r--r--dom/events/test/test_bug574663.html194
-rw-r--r--dom/events/test/test_bug586961.xhtml46
-rw-r--r--dom/events/test/test_bug591249.xhtml73
-rw-r--r--dom/events/test/test_bug591815.html68
-rw-r--r--dom/events/test/test_bug593959.html60
-rw-r--r--dom/events/test/test_bug602962.xhtml85
-rw-r--r--dom/events/test/test_bug603008.html559
-rw-r--r--dom/events/test/test_bug605242.html58
-rw-r--r--dom/events/test/test_bug607464.html84
-rw-r--r--dom/events/test/test_bug613634.html90
-rw-r--r--dom/events/test/test_bug615597.html39
-rw-r--r--dom/events/test/test_bug617528.xhtml96
-rw-r--r--dom/events/test/test_bug624127.html35
-rw-r--r--dom/events/test/test_bug635465.html90
-rw-r--r--dom/events/test/test_bug641477.html37
-rw-r--r--dom/events/test/test_bug648573.html120
-rw-r--r--dom/events/test/test_bug650493.html215
-rw-r--r--dom/events/test/test_bug656379-1.html30
-rw-r--r--dom/events/test/test_bug656379-2.html115
-rw-r--r--dom/events/test/test_bug656954.html42
-rw-r--r--dom/events/test/test_bug659071.html39
-rw-r--r--dom/events/test/test_bug659350.html111
-rw-r--r--dom/events/test/test_bug662678.html153
-rw-r--r--dom/events/test/test_bug667612.html38
-rw-r--r--dom/events/test/test_bug667919-1.html41
-rw-r--r--dom/events/test/test_bug679494.xhtml36
-rw-r--r--dom/events/test/test_bug684208.html80
-rw-r--r--dom/events/test/test_bug687787.html610
-rw-r--r--dom/events/test/test_bug689564.html65
-rw-r--r--dom/events/test/test_bug698929.html47
-rw-r--r--dom/events/test/test_bug704423.html40
-rw-r--r--dom/events/test/test_bug741666.html185
-rw-r--r--dom/events/test/test_bug812744.html37
-rw-r--r--dom/events/test/test_bug822898.html343
-rw-r--r--dom/events/test/test_bug855741.html90
-rw-r--r--dom/events/test/test_bug864040.html87
-rw-r--r--dom/events/test/test_bug924087.html45
-rw-r--r--dom/events/test/test_bug930374-chrome.html57
-rw-r--r--dom/events/test/test_bug930374-content.html70
-rw-r--r--dom/events/test/test_bug944011.html52
-rw-r--r--dom/events/test/test_bug944847.html42
-rw-r--r--dom/events/test/test_bug946632.html156
-rw-r--r--dom/events/test/test_bug967796.html236
-rw-r--r--dom/events/test/test_bug985988.html89
-rw-r--r--dom/events/test/test_bug998809.html35
-rw-r--r--dom/events/test/test_click_on_reframed_generated_text.html32
-rw-r--r--dom/events/test/test_click_on_restyled_element.html51
-rw-r--r--dom/events/test/test_clickevent_on_input.html108
-rw-r--r--dom/events/test/test_coalesce_mousewheel.html241
-rw-r--r--dom/events/test/test_coalesce_touchmove.html34
-rw-r--r--dom/events/test/test_continuous_wheel_events.html3291
-rw-r--r--dom/events/test/test_dblclick_explicit_original_target.html52
-rw-r--r--dom/events/test/test_deltaMode_lines_always_enabled.html40
-rw-r--r--dom/events/test/test_deviceSensor.html136
-rw-r--r--dom/events/test/test_disabled_events.html40
-rw-r--r--dom/events/test/test_dnd_with_modifiers.html78
-rw-r--r--dom/events/test/test_dom_activate_event.html91
-rw-r--r--dom/events/test/test_dom_keyboard_event.html541
-rw-r--r--dom/events/test/test_dom_mouse_event.html143
-rw-r--r--dom/events/test/test_dom_storage_event.html62
-rw-r--r--dom/events/test/test_dom_wheel_event.html835
-rw-r--r--dom/events/test/test_drag_coords.html52
-rw-r--r--dom/events/test/test_drag_image_file.html56
-rw-r--r--dom/events/test/test_draggableprop.html93
-rw-r--r--dom/events/test/test_dragstart.html716
-rw-r--r--dom/events/test/test_error_events.html71
-rw-r--r--dom/events/test/test_eventTimeStamp.html116
-rw-r--r--dom/events/test/test_event_handler_cc.html90
-rw-r--r--dom/events/test/test_event_screenXY_in_cross_origin_iframe.html101
-rw-r--r--dom/events/test/test_event_screenXY_with_zoom.html63
-rw-r--r--dom/events/test/test_eventctors.html930
-rw-r--r--dom/events/test/test_eventctors.xhtml49
-rw-r--r--dom/events/test/test_eventctors_sensors.html97
-rw-r--r--dom/events/test/test_eventhandler_scoping.html17
-rw-r--r--dom/events/test/test_focus_abspos.html32
-rw-r--r--dom/events/test/test_focus_blur_on_click_in_cross_origin_iframe.html118
-rw-r--r--dom/events/test/test_focus_blur_on_click_in_deep_cross_origin_iframe.html146
-rw-r--r--dom/events/test/test_hover_mouseleave.html47
-rw-r--r--dom/events/test/test_legacy_event.html297
-rw-r--r--dom/events/test/test_legacy_touch_api.html65
-rw-r--r--dom/events/test/test_marquee_events.html31
-rw-r--r--dom/events/test/test_messageEvent.html79
-rw-r--r--dom/events/test/test_messageEvent_init.html25
-rw-r--r--dom/events/test/test_mouse_capture_iframe.html70
-rw-r--r--dom/events/test/test_mouse_enterleave_iframe.html362
-rw-r--r--dom/events/test/test_mouse_over_at_removing_down_target.html75
-rw-r--r--dom/events/test/test_moving_and_expanding_selection_per_page.html353
-rw-r--r--dom/events/test/test_moz_mouse_pixel_scroll_event.html478
-rw-r--r--dom/events/test/test_offsetxy.html98
-rw-r--r--dom/events/test/test_onerror_handler_args.html36
-rw-r--r--dom/events/test/test_passive_listeners.html118
-rw-r--r--dom/events/test/test_scroll_per_page.html259
-rw-r--r--dom/events/test/test_selection_after_right_click.html499
-rw-r--r--dom/events/test/test_slotted_mouse_event.html102
-rw-r--r--dom/events/test/test_slotted_text_click.html72
-rw-r--r--dom/events/test/test_submitevent_on_form.html67
-rw-r--r--dom/events/test/test_text_event_in_content.html68
-rw-r--r--dom/events/test/test_unbound_before_in_active_chain.html38
-rw-r--r--dom/events/test/test_use_conflated_keypress_event_model_on_newer_Office_Online_Server.html59
-rw-r--r--dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html82
-rw-r--r--dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html69
-rw-r--r--dom/events/test/test_wheel_default_action.html39
-rw-r--r--dom/events/test/test_wheel_zoom_on_form_controls.html69
-rw-r--r--dom/events/test/window_bug1369072.html170
-rw-r--r--dom/events/test/window_bug1412775.xhtml8
-rw-r--r--dom/events/test/window_bug1429572.html351
-rw-r--r--dom/events/test/window_bug1447993.html239
-rw-r--r--dom/events/test/window_bug493251.html13
-rw-r--r--dom/events/test/window_bug617528.xhtml9
-rw-r--r--dom/events/test/window_bug659071.html85
-rw-r--r--dom/events/test/window_empty_document.html7
-rw-r--r--dom/events/test/window_wheel_default_action.html3618
371 files changed, 44704 insertions, 0 deletions
diff --git a/dom/events/test/browser.toml b/dom/events/test/browser.toml
new file mode 100644
index 0000000000..d1f718c05c
--- /dev/null
+++ b/dom/events/test/browser.toml
@@ -0,0 +1,33 @@
+[DEFAULT]
+
+["browser_alt_keyup_in_content.js"]
+skip-if = ["os == 'mac'"]
+
+["browser_beforeinput_by_execCommand_in_contentscript.js"]
+support-files = [
+ "file_beforeinput_by_execCommand_in_contentscript.html",
+ "../../../browser/base/content/test/general/head.js",
+]
+
+["browser_bug1539497.js"]
+
+["browser_dragimage.js"]
+support-files = [
+ "dragimage.html",
+ "green.png",
+]
+
+["browser_keyboard_event_init_key_event_enabled_in_contentscript.js"]
+support-files = [
+ "file_keyboard_event_init_key_event_enabled_in_contentscript.html",
+ "../../../browser/base/content/test/general/head.js",
+]
+
+["browser_mouse_enterleave_switch_tab.js"]
+support-files = ["../../../browser/base/content/test/general/dummy_page.html"]
+fail-if = ["a11y_checks"] # Bug 1854502 clicked tabbrowser-tabpanels may not be accessible
+
+["browser_mouseout_notification_panel.js"]
+
+["browser_shortcutkey_modifier_conflicts_with_content_accesskey_modifier.js"]
+skip-if = ["os == 'mac'"] # Alt + D is defined only on Linux and Windows
diff --git a/dom/events/test/browser_alt_keyup_in_content.js b/dom/events/test/browser_alt_keyup_in_content.js
new file mode 100644
index 0000000000..746e442d65
--- /dev/null
+++ b/dom/events/test/browser_alt_keyup_in_content.js
@@ -0,0 +1,333 @@
+"use strict";
+
+add_task(async function runTests() {
+ const menubar = document.getElementById("toolbar-menubar");
+ const autohide = menubar.getAttribute("autohide");
+ // This test requires that the window is active because of the limitation of
+ // menubar. Therefore, we should abort if the window becomes inactive during
+ // the tests.
+ let runningTests = true;
+ function onWindowActive(aEvent) {
+ // Don't warn after timed out.
+ if (runningTests && aEvent.target === window) {
+ info(
+ "WARNING: This window shouldn't have been inactivated during tests, but received an activated event!"
+ );
+ }
+ }
+ function onWindowInactive(aEvent) {
+ // Don't warn after timed out.
+ if (runningTests && aEvent.target === window) {
+ info(
+ "WARNING: This window should be active during tests, but inactivated!"
+ );
+ window.focus();
+ }
+ }
+ let menubarActivated = false;
+ function onMenubarActive() {
+ menubarActivated = true;
+ }
+ // In this test, menu popups shouldn't be open, but this helps avoiding
+ // intermittent failure after inactivating the menubar.
+ let popupEvents = 0;
+ function getPopupInfo(aPopupEventTarget) {
+ return `<${
+ aPopupEventTarget.nodeName
+ }${aPopupEventTarget.getAttribute("id") !== null ? ` id="${aPopupEventTarget.getAttribute("id")}"` : ""}>`;
+ }
+ function onPopupShown(aEvent) {
+ // Don't warn after timed out.
+ if (!runningTests) {
+ return;
+ }
+ popupEvents++;
+ info(
+ `A popup (${getPopupInfo(
+ aEvent.target
+ )}) is shown (visible popups: ${popupEvents})`
+ );
+ }
+ function onPopupHidden(aEvent) {
+ // Don't warn after timed out.
+ if (!runningTests) {
+ return;
+ }
+ if (popupEvents === 0) {
+ info(
+ `WARNING: There are some unexpected popups which may be not cleaned up by the previous test (${getPopupInfo(
+ aEvent.target
+ )})`
+ );
+ return;
+ }
+ popupEvents--;
+ info(
+ `A popup (${getPopupInfo(
+ aEvent.target
+ )}) is hidden (visible popups: ${popupEvents})`
+ );
+ }
+ try {
+ Services.prefs.setBoolPref("ui.key.menuAccessKeyFocuses", true);
+ // If this fails, you need to replace "KEY_Alt" with a variable whose
+ // value is considered from the pref.
+ is(
+ Services.prefs.getIntPref("ui.key.menuAccessKey"),
+ 18,
+ "This test assumes that Alt key activates the menubar"
+ );
+ window.addEventListener("activate", onWindowActive);
+ window.addEventListener("deactivate", onWindowInactive);
+ window.addEventListener("popupshown", onPopupShown);
+ window.addEventListener("popuphidden", onPopupHidden);
+ menubar.addEventListener("DOMMenuBarActive", onMenubarActive);
+ async function doTest(aTest) {
+ await new Promise(resolve => {
+ if (Services.focus.activeWindow === window) {
+ resolve();
+ return;
+ }
+ info(
+ `${aTest.description}: The testing window is inactive, trying to activate it...`
+ );
+ Services.focus.focusedWindow = window;
+ TestUtils.waitForCondition(() => {
+ if (Services.focus.activeWindow === window) {
+ resolve();
+ return true;
+ }
+ Services.focus.focusedWindow = window;
+ return false;
+ }, `${aTest.description}: Waiting the window is activated`);
+ });
+ let startTime = performance.now();
+ info(`Start to test: ${aTest.description}...`);
+
+ async function ensureMenubarInactive() {
+ if (!menubar.querySelector("[_moz-menuactive=true]")) {
+ return;
+ }
+ info(`${aTest.description}: Inactivating the menubar...`);
+ let waitForMenuBarInactive = BrowserTestUtils.waitForEvent(
+ menubar,
+ "DOMMenuBarInactive"
+ );
+ EventUtils.synthesizeKey("KEY_Escape", {}, window);
+ await waitForMenuBarInactive;
+ await TestUtils.waitForCondition(() => {
+ return popupEvents === 0;
+ }, `${aTest.description}: Waiting for closing all popups`);
+ }
+
+ try {
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ url: aTest.url,
+ },
+ async browser => {
+ info(`${aTest.description}: Waiting browser getting focus...`);
+ await SimpleTest.promiseFocus(browser);
+ await ensureMenubarInactive();
+ menubarActivated = false;
+
+ let keyupEventFiredInContent = false;
+ BrowserTestUtils.addContentEventListener(
+ browser,
+ "keyup",
+ () => {
+ keyupEventFiredInContent = true;
+ },
+ { capture: true },
+ event => {
+ return event.key === "Alt";
+ }
+ );
+
+ // For making sure adding the above content event listener and
+ // it'll get `keyup` event, let's run `SpecialPowers.spawn` and
+ // wait for focus in the content process.
+ info(
+ `${aTest.description}: Waiting content process getting focus...`
+ );
+ await SpecialPowers.spawn(
+ browser,
+ [aTest.description],
+ async aTestDescription => {
+ await ContentTaskUtils.waitForCondition(() => {
+ if (
+ content.browsingContext.isActive &&
+ content.document.hasFocus()
+ ) {
+ return true;
+ }
+ content.window.focus();
+ return false;
+ }, `${aTestDescription}: Waiting for content gets focus in content process`);
+ }
+ );
+
+ let waitForAllKeyUpEventsInChrome = new Promise(resolve => {
+ // Wait 2 `keyup` events in the main process. First one is
+ // synthesized one. The other is replay event from content.
+ let firstKeyUpEvent;
+ window.addEventListener(
+ "keyup",
+ function onKeyUpInChrome(event) {
+ if (!firstKeyUpEvent) {
+ firstKeyUpEvent = event;
+ return;
+ }
+ window.removeEventListener("keyup", onKeyUpInChrome, {
+ capture: true,
+ });
+ resolve();
+ },
+ { capture: true }
+ );
+ });
+
+ let menubarActivatedPromise;
+ if (aTest.expectMenubarActive) {
+ menubarActivatedPromise = BrowserTestUtils.waitForEvent(
+ menubar,
+ "DOMMenuBarActive"
+ );
+ }
+
+ EventUtils.synthesizeKey("KEY_Alt", {}, window);
+ info(
+ `${aTest.description}: Waiting keyup events of Alt in chrome...`
+ );
+ await waitForAllKeyUpEventsInChrome;
+ info(`${aTest.description}: Waiting keyup event in content...`);
+ try {
+ await TestUtils.waitForCondition(() => {
+ return keyupEventFiredInContent;
+ }, `${aTest.description}: Waiting for content gets focus in chrome process`);
+ } catch (ex) {
+ ok(
+ false,
+ `${aTest.description}: Failed to synthesize Alt key press in the content process`
+ );
+ return;
+ }
+
+ if (aTest.expectMenubarActive) {
+ await menubarActivatedPromise;
+ ok(
+ menubarActivated,
+ `${aTest.description}: Menubar should've been activated by the synthesized Alt key press`
+ );
+ } else {
+ // Wait some ticks to verify not receiving "DOMMenuBarActive" event.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 500));
+ ok(
+ !menubarActivated,
+ `${aTest.description}: Menubar should not have been activated by the synthesized Alt key press`
+ );
+ }
+ }
+ );
+ } catch (ex) {
+ ok(
+ false,
+ `${aTest.description}: Thrown an exception: ${ex.toString()}`
+ );
+ } finally {
+ await ensureMenubarInactive();
+ info(`End testing: ${aTest.description}`);
+ ChromeUtils.addProfilerMarker(
+ "browser-test",
+ { startTime, category: "Test" },
+ aTest.description
+ );
+ }
+ }
+
+ // Testcases for users who use collapsible menubar (by default)
+ menubar.setAttribute("autohide", "true");
+ await doTest({
+ description: "Testing menubar is shown by Alt keyup",
+ url: "data:text/html;charset=utf-8,<p>static page</p>",
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is shown by Alt keyup when an <input> has focus",
+ url:
+ "data:text/html;charset=utf-8,<input>" +
+ '<script>document.querySelector("input").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is shown by Alt keyup when an editing host has focus",
+ url:
+ "data:text/html;charset=utf-8,<p contenteditable></p>" +
+ '<script>document.querySelector("p[contenteditable]").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar won't be shown by Alt keyup due to suppressed by the page",
+ url:
+ "data:text/html;charset=utf-8,<p>dynamic page</p>" +
+ '<script>window.addEventListener("keyup", event => { event.preventDefault(); })</script>',
+ expectMenubarActive: false,
+ });
+
+ // Testcases for users who always show the menubar.
+ menubar.setAttribute("autohide", "false");
+ await doTest({
+ description: "Testing menubar is activated by Alt keyup",
+ url: "data:text/html;charset=utf-8,<p>static page</p>",
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is activated by Alt keyup when an <input> has focus",
+ url:
+ "data:text/html;charset=utf-8,<input>" +
+ '<script>document.querySelector("input").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar is activated by Alt keyup when an editing host has focus",
+ url:
+ "data:text/html;charset=utf-8,<p contenteditable></p>" +
+ '<script>document.querySelector("p[contenteditable]").focus()</script>',
+ expectMenubarActive: true,
+ });
+ await doTest({
+ description:
+ "Testing menubar won't be activated by Alt keyup due to suppressed by the page",
+ url:
+ "data:text/html;charset=utf-8,<p>dynamic page</p>" +
+ '<script>window.addEventListener("keyup", event => { event.preventDefault(); })</script>',
+ expectMenubarActive: false,
+ });
+ runningTests = false;
+ } catch (ex) {
+ ok(
+ false,
+ `Aborting this test due to unexpected the exception (${ex.toString()})`
+ );
+ runningTests = false;
+ } finally {
+ if (autohide !== null) {
+ menubar.setAttribute("autohide", autohide);
+ } else {
+ menubar.removeAttribute("autohide");
+ }
+ Services.prefs.clearUserPref("ui.key.menuAccessKeyFocuses");
+ menubar.removeEventListener("DOMMenuBarActive", onMenubarActive);
+ window.removeEventListener("activate", onWindowActive);
+ window.removeEventListener("deactivate", onWindowInactive);
+ window.removeEventListener("popupshown", onPopupShown);
+ window.removeEventListener("popuphidden", onPopupHidden);
+ }
+});
diff --git a/dom/events/test/browser_beforeinput_by_execCommand_in_contentscript.js b/dom/events/test/browser_beforeinput_by_execCommand_in_contentscript.js
new file mode 100644
index 0000000000..d5e4ef1bbf
--- /dev/null
+++ b/dom/events/test/browser_beforeinput_by_execCommand_in_contentscript.js
@@ -0,0 +1,195 @@
+"use strict";
+
+async function installAndStartExtension() {
+ function contentScript() {
+ window.addEventListener("keydown", aEvent => {
+ console.log("keydown event is fired");
+ if (aEvent.defaultPrevented) {
+ return;
+ }
+ let selection = window.getSelection();
+ if (selection.isCollapsed) {
+ return;
+ }
+ if (aEvent.ctrlKey && aEvent.key === "k") {
+ document.execCommand("createLink", false, "http://example.com/");
+ aEvent.preventDefault();
+ }
+ });
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ js: ["content_script.js"],
+ matches: ["<all_urls>"],
+ run_at: "document_start",
+ },
+ ],
+ },
+ files: {
+ "content_script.js": contentScript,
+ },
+ });
+
+ await extension.startup();
+
+ return extension;
+}
+
+add_task(async function () {
+ const extension = await installAndStartExtension();
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/browser/dom/events/test/file_beforeinput_by_execCommand_in_contentscript.html",
+ true
+ );
+
+ /**
+ * Document.execCommand() shouldn't cause `beforeinput`, but it may be used
+ * by addons for emulating user input and make the input undoable on builtin
+ * editors. Therefore, if and only if it's called by addons, `beforeinput`
+ * should be fired.
+ */
+ function runTest() {
+ const editor = content.document.querySelector("[contenteditable]");
+ editor.focus();
+ content.document.getSelection().selectAllChildren(editor);
+ let beforeinput;
+ editor.addEventListener("beforeinput", aEvent => {
+ beforeinput = aEvent;
+ });
+ const description = 'Test execCommand("createLink")';
+ editor.addEventListener("input", aEvent => {
+ if (!beforeinput) {
+ sendAsyncMessage("Test:BeforeInputInContentEditable", {
+ succeeded: false,
+ message: `${description}: No beforeinput event is fired`,
+ });
+ return;
+ }
+ sendAsyncMessage("Test:BeforeInputInContentEditable", {
+ succeeded:
+ editor.innerHTML === '<a href="http://example.com/">abcdef</a>',
+ message: `${description}: editor.innerHTML=${editor.innerHTML}`,
+ });
+ });
+ }
+
+ try {
+ tab.linkedBrowser.messageManager.loadFrameScript(
+ "data:,(" + runTest.toString() + ")();",
+ false
+ );
+
+ let testResult = new Promise(resolve => {
+ let mm = tab.linkedBrowser.messageManager;
+ mm.addMessageListener(
+ "Test:BeforeInputInContentEditable",
+ function onFinish(aMsg) {
+ mm.removeMessageListener(
+ "Test:BeforeInputInContentEditable",
+ onFinish
+ );
+ is(aMsg.data.succeeded, true, aMsg.data.message);
+ resolve();
+ }
+ );
+ });
+ info("Sending Ctrl+K...");
+ await BrowserTestUtils.synthesizeKey(
+ "k",
+ { ctrlKey: true },
+ tab.linkedBrowser
+ );
+ info("Waiting test result...");
+ await testResult;
+ } finally {
+ BrowserTestUtils.removeTab(tab);
+ await extension.unload();
+ }
+});
+
+add_task(async function () {
+ const extension = await installAndStartExtension();
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/browser/dom/events/test/file_beforeinput_by_execCommand_in_contentscript.html",
+ true
+ );
+
+ /**
+ * Document.execCommand() from addons should be treated as a user input.
+ * Therefore, it should not block first nested Document.execCommand() call
+ * in a "beforeinput" event listener in the web app.
+ */
+ function runTest() {
+ const editor = content.document.querySelectorAll("[contenteditable]")[1];
+ editor.focus();
+ content.document.getSelection().selectAllChildren(editor);
+ const beforeInputs = [];
+ editor.parentNode.addEventListener(
+ "beforeinput",
+ aEvent => {
+ beforeInputs.push(aEvent);
+ },
+ { capture: true }
+ );
+ const description =
+ 'Test web app calls execCommand("insertText") on "beforeinput"';
+ editor.addEventListener("input", aEvent => {
+ if (!beforeInputs.length) {
+ sendAsyncMessage("Test:BeforeInputInContentEditable", {
+ succeeded: false,
+ message: `${description}: No beforeinput event is fired`,
+ });
+ return;
+ }
+ if (beforeInputs.length > 1) {
+ sendAsyncMessage("Test:BeforeInputInContentEditable", {
+ succeeded: false,
+ message: `${description}: Too many beforeinput events are fired`,
+ });
+ return;
+ }
+ sendAsyncMessage("Test:BeforeInputInContentEditable", {
+ succeeded: editor.innerHTML.replace("<br>", "") === "ABCDEF",
+ message: `${description}: editor.innerHTML=${editor.innerHTML}`,
+ });
+ });
+ }
+
+ try {
+ tab.linkedBrowser.messageManager.loadFrameScript(
+ "data:,(" + runTest.toString() + ")();",
+ false
+ );
+
+ let testResult = new Promise(resolve => {
+ let mm = tab.linkedBrowser.messageManager;
+ mm.addMessageListener(
+ "Test:BeforeInputInContentEditable",
+ function onFinish(aMsg) {
+ mm.removeMessageListener(
+ "Test:BeforeInputInContentEditable",
+ onFinish
+ );
+ is(aMsg.data.succeeded, true, aMsg.data.message);
+ resolve();
+ }
+ );
+ });
+ info("Sending Ctrl+K...");
+ await BrowserTestUtils.synthesizeKey(
+ "k",
+ { ctrlKey: true },
+ tab.linkedBrowser
+ );
+ info("Waiting test result...");
+ await testResult;
+ } finally {
+ BrowserTestUtils.removeTab(tab);
+ await extension.unload();
+ }
+});
diff --git a/dom/events/test/browser_bug1539497.js b/dom/events/test/browser_bug1539497.js
new file mode 100644
index 0000000000..78ea64c119
--- /dev/null
+++ b/dom/events/test/browser_bug1539497.js
@@ -0,0 +1,25 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function maxTouchPoints() {
+ await new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["dom.maxtouchpoints.testing.value", 5]],
+ },
+ resolve
+ );
+ });
+
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,Test page for navigator.maxTouchPoints"
+ );
+ await SpecialPowers.spawn(tab.linkedBrowser, [], function () {
+ is(content.navigator.maxTouchPoints, 5, "Should have touch points.");
+ });
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/events/test/browser_dragimage.js b/dom/events/test/browser_dragimage.js
new file mode 100644
index 0000000000..503c0ac449
--- /dev/null
+++ b/dom/events/test/browser_dragimage.js
@@ -0,0 +1,65 @@
+const TEST_URI = "dragimage.html";
+
+// This test checks that dragging an image onto the same document
+// does not drop it, even when the page cancels the dragover event.
+add_task(async function dragimage_remote_tab() {
+ var tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://www.example.com/browser/dom/events/test/" + TEST_URI
+ );
+
+ let dropHappened = false;
+ let oldHandler = tab.linkedBrowser.droppedLinkHandler;
+ tab.linkedBrowser.droppedLinkHandler = () => {
+ dropHappened = true;
+ };
+
+ await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ let image = content.document.body.firstElementChild;
+ let target = content.document.body.lastElementChild;
+
+ await EventUtils.synthesizePlainDragAndDrop({
+ srcElement: image,
+ destElement: target,
+ srcWindow: content,
+ destWindow: content,
+ id: content.windowUtils.DEFAULT_MOUSE_POINTER_ID,
+ });
+ });
+
+ tab.linkedBrowser.droppedLinkHandler = oldHandler;
+
+ ok(!dropHappened, "drop did not occur");
+
+ BrowserTestUtils.removeTab(tab);
+});
+
+// This test checks the same but with an in-process page.
+add_task(async function dragimage_local_tab() {
+ var tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ getRootDirectory(gTestPath) + TEST_URI
+ );
+
+ let dropHappened = false;
+ let oldHandler = tab.linkedBrowser.droppedLinkHandler;
+ tab.linkedBrowser.droppedLinkHandler = () => {
+ dropHappened = true;
+ };
+
+ let image = tab.linkedBrowser.contentDocument.body.firstElementChild;
+ let target = tab.linkedBrowser.contentDocument.body.lastElementChild;
+
+ await EventUtils.synthesizePlainDragAndDrop({
+ srcElement: image,
+ destElement: target,
+ srcWindow: tab.linkedBrowser.contentWindow,
+ destWindow: tab.linkedBrowser.contentWindow,
+ });
+
+ tab.linkedBrowser.droppedLinkHandler = oldHandler;
+
+ ok(!dropHappened, "drop did not occur");
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/events/test/browser_keyboard_event_init_key_event_enabled_in_contentscript.js b/dom/events/test/browser_keyboard_event_init_key_event_enabled_in_contentscript.js
new file mode 100644
index 0000000000..e8d51edb3e
--- /dev/null
+++ b/dom/events/test/browser_keyboard_event_init_key_event_enabled_in_contentscript.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+async function installAndStartExtension() {
+ function contentScript() {
+ window.addEventListener(
+ "load",
+ () => {
+ document.documentElement.setAttribute(
+ "data-initKeyEvent-in-contentscript",
+ typeof window.KeyboardEvent.prototype.initKeyEvent
+ );
+ },
+ { capture: true, once: true }
+ );
+ }
+
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ js: ["content_script.js"],
+ matches: ["<all_urls>"],
+ run_at: "document_start",
+ },
+ ],
+ },
+ files: {
+ "content_script.js": contentScript,
+ },
+ });
+
+ await extension.startup();
+
+ return extension;
+}
+
+add_task(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.keyboardevent.init_key_event.enabled", false],
+ ["dom.keyboardevent.init_key_event.enabled_in_addons", true],
+ ],
+ });
+
+ const extension = await installAndStartExtension();
+ await BrowserTestUtils.withNewTab(
+ "http://example.com/browser/dom/events/test/file_keyboard_event_init_key_event_enabled_in_contentscript.html",
+ async browser => {
+ info("Waiting for the test result...");
+ await SpecialPowers.spawn(browser, [], () => {
+ Assert.equal(
+ content.document.documentElement.getAttribute(
+ "data-initKeyEvent-before"
+ ),
+ "undefined",
+ "KeyboardEvent.initKeyEvent shouldn't be available in web-content"
+ );
+ Assert.equal(
+ content.document.documentElement.getAttribute(
+ "data-initKeyEvent-in-contentscript"
+ ),
+ "function",
+ "KeyboardEvent.initKeyEvent should be available in contentscript"
+ );
+ Assert.equal(
+ content.document.documentElement.getAttribute(
+ "data-initKeyEvent-after"
+ ),
+ "undefined",
+ "KeyboardEvent.initKeyEvent shouldn't be available in web-content even after contentscript accesses it"
+ );
+ });
+ }
+ );
+
+ await extension.unload();
+});
diff --git a/dom/events/test/browser_mouse_enterleave_switch_tab.js b/dom/events/test/browser_mouse_enterleave_switch_tab.js
new file mode 100644
index 0000000000..9c24120101
--- /dev/null
+++ b/dom/events/test/browser_mouse_enterleave_switch_tab.js
@@ -0,0 +1,158 @@
+"use strict";
+
+async function synthesizeMouseAndWait(aBrowser, aEvent) {
+ let promise = SpecialPowers.spawn(aBrowser, [aEvent], async event => {
+ await new Promise(resolve => {
+ content.document.documentElement.addEventListener(event, resolve, {
+ once: true,
+ });
+ });
+ });
+ // Ensure content has been added event listener.
+ await SpecialPowers.spawn(aBrowser, [], () => {});
+ EventUtils.synthesizeMouse(aBrowser, 10, 10, { type: aEvent });
+ return promise;
+}
+
+function AddMouseEventListener(aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [], () => {
+ content.catchedEvents = [];
+ let listener = function (aEvent) {
+ content.catchedEvents.push(aEvent.type);
+ };
+
+ let target = content.document.querySelector("p");
+ target.onmouseenter = listener;
+ target.onmouseleave = listener;
+ });
+}
+
+function clearMouseEventListenerAndCheck(aBrowser, aExpectedEvents) {
+ return SpecialPowers.spawn(aBrowser, [aExpectedEvents], events => {
+ let target = content.document.querySelector("p");
+ target.onmouseenter = null;
+ target.onmouseleave = null;
+
+ Assert.deepEqual(content.catchedEvents, events);
+ });
+}
+
+add_task(async function testSwitchTabs() {
+ const tabFirst = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/browser/browser/base/content/test/general/dummy_page.html",
+ true
+ );
+
+ info("Initial mouse move");
+ await EventUtils.synthesizeAndWaitNativeMouseMove(
+ tabFirst.linkedBrowser,
+ 10,
+ 10
+ );
+
+ info("Open and move to a new tab");
+ await AddMouseEventListener(tabFirst.linkedBrowser);
+ const tabSecond = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/browser/browser/base/content/test/general/dummy_page.html"
+ );
+ // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
+ // events.
+ await EventUtils.synthesizeAndWaitNativeMouseMove(
+ tabSecond.linkedBrowser,
+ 10,
+ 10
+ );
+ // Wait a bit to see if there is any unexpected mouse event.
+ await TestUtils.waitForTick();
+ await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseleave"]);
+
+ info("switch back to the previous tab");
+ await AddMouseEventListener(tabFirst.linkedBrowser);
+ await AddMouseEventListener(tabSecond.linkedBrowser);
+ await BrowserTestUtils.switchTab(gBrowser, tabFirst);
+ // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
+ // events.
+ await EventUtils.synthesizeAndWaitNativeMouseMove(
+ tabFirst.linkedBrowser,
+ 10,
+ 10
+ );
+ // Wait a bit to see if there is any unexpected mouse event.
+ await TestUtils.waitForTick();
+ await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseenter"]);
+ await clearMouseEventListenerAndCheck(tabSecond.linkedBrowser, [
+ "mouseleave",
+ ]);
+
+ info("Close tabs");
+ BrowserTestUtils.removeTab(tabFirst);
+ BrowserTestUtils.removeTab(tabSecond);
+});
+
+add_task(async function testSwitchTabsWithMouseDown() {
+ const tabFirst = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/browser/browser/base/content/test/general/dummy_page.html",
+ true
+ );
+
+ info("Initial mouse move");
+ await EventUtils.synthesizeAndWaitNativeMouseMove(
+ tabFirst.linkedBrowser,
+ 10,
+ 10
+ );
+
+ info("mouse down");
+ await synthesizeMouseAndWait(tabFirst.linkedBrowser, "mousedown");
+
+ info("Open and move to a new tab");
+ await AddMouseEventListener(tabFirst.linkedBrowser);
+ const tabSecond = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "http://example.com/browser/browser/base/content/test/general/dummy_page.html"
+ );
+ // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
+ // events.
+ await EventUtils.synthesizeAndWaitNativeMouseMove(
+ tabSecond.linkedBrowser,
+ 10,
+ 10
+ );
+
+ info("mouse up");
+ await synthesizeMouseAndWait(tabSecond.linkedBrowser, "mouseup");
+ // Wait a bit to see if there is any unexpected mouse event.
+ await TestUtils.waitForTick();
+ await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseleave"]);
+
+ info("mouse down");
+ await synthesizeMouseAndWait(tabSecond.linkedBrowser, "mousedown");
+
+ info("switch back to the previous tab");
+ await AddMouseEventListener(tabFirst.linkedBrowser);
+ await AddMouseEventListener(tabSecond.linkedBrowser);
+ await BrowserTestUtils.switchTab(gBrowser, tabFirst);
+ // Synthesize a mousemove to generate corresponding mouseenter and mouseleave
+ // events.
+ await EventUtils.synthesizeAndWaitNativeMouseMove(
+ tabFirst.linkedBrowser,
+ 10,
+ 10
+ );
+
+ info("mouse up");
+ await synthesizeMouseAndWait(tabFirst.linkedBrowser, "mouseup");
+ // Wait a bit to see if there is any unexpected mouse event.
+ await TestUtils.waitForTick();
+ await clearMouseEventListenerAndCheck(tabFirst.linkedBrowser, ["mouseenter"]);
+ await clearMouseEventListenerAndCheck(tabSecond.linkedBrowser, [
+ "mouseleave",
+ ]);
+
+ info("Close tabs");
+ BrowserTestUtils.removeTab(tabFirst);
+ BrowserTestUtils.removeTab(tabSecond);
+});
diff --git a/dom/events/test/browser_mouseout_notification_panel.js b/dom/events/test/browser_mouseout_notification_panel.js
new file mode 100644
index 0000000000..4bd7b93afc
--- /dev/null
+++ b/dom/events/test/browser_mouseout_notification_panel.js
@@ -0,0 +1,145 @@
+"use strict";
+
+async function showNotification(aBrowser, aId) {
+ info(`Show notification ${aId}`);
+ let promise = BrowserTestUtils.waitForEvent(
+ PopupNotifications.panel,
+ "popupshown"
+ );
+ let notification = PopupNotifications.show(
+ aBrowser /* browser */,
+ "test-notification-" + aId /* id */,
+ aId + ": Will you allow <> to perform this action?" /* message */,
+ null /* anchorID */,
+ {
+ label: "Main Action",
+ accessKey: "M",
+ callback: () => {},
+ } /* mainAction */,
+ [
+ {
+ label: "Secondary Action",
+ accessKey: "S",
+ callback: () => {},
+ },
+ ] /* secondaryActions */
+ );
+ await promise;
+
+ let rect = PopupNotifications.panel.getBoundingClientRect();
+ return { notification, rect };
+}
+
+function waitForMouseEvent(aType, aElement) {
+ return new Promise(resolve => {
+ aElement.addEventListener(
+ aType,
+ e => {
+ resolve({
+ screenX: e.screenX,
+ screenY: e.screenY,
+ clientX: e.clientX,
+ clientY: e.clientY,
+ });
+ },
+ { once: true }
+ );
+ });
+}
+
+function waitForRemoteMouseEvent(aType, aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [aType], async aType => {
+ return new Promise(
+ resolve => {
+ content.document.addEventListener(aType, e => {
+ resolve({
+ screenX: e.screenX,
+ screenY: e.screenY,
+ clientX: e.clientX,
+ clientY: e.clientY,
+ });
+ });
+ },
+ { once: true }
+ );
+ });
+}
+
+function synthesizeMouseAtCenter(aRect) {
+ EventUtils.synthesizeMouseAtPoint(
+ aRect.left + aRect.width / 2,
+ aRect.top + aRect.height / 2,
+ {
+ type: "mousemove",
+ }
+ );
+}
+
+let notificationRect;
+
+add_setup(async function init() {
+ ok(PopupNotifications, "PopupNotifications object exists");
+ ok(PopupNotifications.panel, "PopupNotifications panel exists");
+
+ await SpecialPowers.pushPrefEnv({
+ set: [["test.events.async.enabled", true]],
+ });
+
+ info(`Show notification to get its size and position`);
+ let { notification, rect } = await showNotification(
+ gBrowser.selectedBrowser,
+ "Test#Init"
+ );
+ PopupNotifications.remove(notification);
+ notificationRect = rect;
+});
+
+add_task(async function test_mouseout_chrome() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ info(`Generate mousemove event on browser`);
+ let mousemovePromise = waitForMouseEvent("mousemove", browser);
+ synthesizeMouseAtCenter(notificationRect);
+ let mousemoveCoordinate = await mousemovePromise;
+ info(`mousemove event: ${JSON.stringify(mousemoveCoordinate)}`);
+
+ info(`Showing notification should generate mouseout event on browser`);
+ let mouseoutPromise = waitForMouseEvent("mouseout", browser);
+ let { notification } = await showNotification(browser, "Test#Chrome");
+ let mouseoutCoordinate = await mouseoutPromise;
+ info(`mouseout event: ${JSON.stringify(mouseoutCoordinate)}`);
+
+ SimpleTest.isDeeply(
+ mouseoutCoordinate,
+ mousemoveCoordinate,
+ "Test event coordinate"
+ );
+ info(`Remove notification`);
+ PopupNotifications.remove(notification);
+ });
+});
+
+add_task(async function test_mouseout_content() {
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ info(`Generate mousemove event on content`);
+ let mousemovePromise = waitForRemoteMouseEvent("mousemove", browser);
+ SimpleTest.executeSoon(() => {
+ synthesizeMouseAtCenter(notificationRect);
+ });
+ let mousemoveCoordinate = await mousemovePromise;
+ info(`mousemove event on content: ${JSON.stringify(mousemoveCoordinate)}`);
+
+ info(`Showing notification should generate mouseout event on content`);
+ let mouseoutPromise = waitForRemoteMouseEvent("mouseout", browser);
+ SimpleTest.executeSoon(async () => {
+ showNotification(browser, "Test#Content");
+ });
+ let mouseoutCoordinate = await mouseoutPromise;
+ info(`remote mouseout event: ${JSON.stringify(mouseoutCoordinate)}`);
+
+ SimpleTest.isDeeply(
+ mouseoutCoordinate,
+ mousemoveCoordinate,
+ "Test event coordinate"
+ );
+ });
+});
diff --git a/dom/events/test/browser_shortcutkey_modifier_conflicts_with_content_accesskey_modifier.js b/dom/events/test/browser_shortcutkey_modifier_conflicts_with_content_accesskey_modifier.js
new file mode 100644
index 0000000000..b12fd3b1df
--- /dev/null
+++ b/dom/events/test/browser_shortcutkey_modifier_conflicts_with_content_accesskey_modifier.js
@@ -0,0 +1,102 @@
+add_task(async function () {
+ // Even if modifier of a shortcut key same as modifier of content access key,
+ // the shortcut key should be executed if (remote) content doesn't handle it.
+ // This test uses existing shortcut key declaration on Linux and Windows.
+ // If you remove or change Alt + D, you need to keep check this with changing
+ // the pref or result check.
+
+ await new Promise(resolve => {
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [
+ ["ui.key.generalAccessKey", -1],
+ ["ui.key.chromeAccess", 0 /* disabled */],
+ ["ui.key.contentAccess", 4 /* Alt */],
+ ["browser.search.widget.inNavBar", true],
+ ],
+ },
+ resolve
+ );
+ });
+
+ const kTestPage = "data:text/html,<body>simple web page</body>";
+ let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, kTestPage);
+
+ let searchBar = BrowserSearch.searchBar;
+ searchBar.focus();
+
+ function promiseURLBarHasFocus() {
+ return new Promise(resolve => {
+ if (gURLBar.focused) {
+ ok(true, "The URL bar already has focus");
+ resolve();
+ return;
+ }
+ info("Waiting focus event...");
+ gURLBar.addEventListener(
+ "focus",
+ () => {
+ ok(true, "The URL bar gets focus");
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ }
+
+ function promiseURLBarSelectsAllText() {
+ return new Promise(resolve => {
+ function isAllTextSelected() {
+ return (
+ gURLBar.inputField.selectionStart === 0 &&
+ gURLBar.inputField.selectionEnd === gURLBar.inputField.value.length
+ );
+ }
+ if (isAllTextSelected()) {
+ ok(true, "All text of the URL bar is already selected");
+ isnot(
+ gURLBar.inputField.value,
+ "",
+ "The URL bar should have non-empty text"
+ );
+ resolve();
+ return;
+ }
+ info("Waiting selection changes...");
+ function tryToCheckItLater() {
+ if (!isAllTextSelected()) {
+ SimpleTest.executeSoon(tryToCheckItLater);
+ return;
+ }
+ ok(true, "All text of the URL bar should be selected");
+ isnot(
+ gURLBar.inputField.value,
+ "",
+ "The URL bar should have non-empty text"
+ );
+ resolve();
+ }
+ SimpleTest.executeSoon(tryToCheckItLater);
+ });
+ }
+
+ // Alt + D is a shortcut key to move focus to the URL bar and selects its text.
+ info("Pressing Alt + D in the search bar...");
+ EventUtils.synthesizeKey("d", { altKey: true });
+
+ await promiseURLBarHasFocus();
+ await promiseURLBarSelectsAllText();
+
+ // Alt + D in the URL bar should select all text in it.
+ await gURLBar.focus();
+ await promiseURLBarHasFocus();
+ gURLBar.inputField.selectionStart = gURLBar.inputField.selectionEnd =
+ gURLBar.inputField.value.length;
+
+ info("Pressing Alt + D in the URL bar...");
+ EventUtils.synthesizeKey("d", { altKey: true });
+ await promiseURLBarHasFocus();
+ await promiseURLBarSelectsAllText();
+
+ gBrowser.removeCurrentTab();
+});
diff --git a/dom/events/test/bug1017086_inner.html b/dom/events/test/bug1017086_inner.html
new file mode 100644
index 0000000000..e911d993d5
--- /dev/null
+++ b/dom/events/test/bug1017086_inner.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1017086
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1017086</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="application/javascript">
+ /** Test for Bug 1017086 **/
+ var testelem = undefined;
+ var pointer_events = ["onpointerover", "onpointerenter",
+ "onpointermove",
+ "onpointerdown", "onpointerup",
+ "onpointerout", "onpointerleave",
+ "onpointercancel"];
+ function check(expected_value, event_name, container, container_name) {
+ var text = event_name + " in " + container_name + " should be " + expected_value;
+ parent.is(event_name in container, expected_value, text);
+ }
+ function runTest() {
+ testelem = document.getElementById("test");
+ is(!!testelem, true, "Document should have element with id 'test'");
+ parent.part_of_checks(pointer_events, check, window, document, testelem);
+ }
+ </script>
+</head>
+<body onload="runTest();">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1017086">Mozilla Bug 1017086</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+ </div>
+ <pre id="test">
+ </pre>
+</body>
+</html>
diff --git a/dom/events/test/bug226361_iframe.xhtml b/dom/events/test/bug226361_iframe.xhtml
new file mode 100644
index 0000000000..df38a8bcbe
--- /dev/null
+++ b/dom/events/test/bug226361_iframe.xhtml
@@ -0,0 +1,47 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=226361
+-->
+<head>
+ <title>Test for Bug 226361</title>
+</head>
+<body id="body">
+<p id="display">
+
+<a id="a1" tabindex="3" href="http://home.mozilla.org">This is the 1st
+
+link but the 3rd tabindex</a><br />
+
+<br />
+
+ <a id="a2" tabindex="4" href="http://home.mozilla.org">This is the 2nd
+
+link but the 4th tabindex</a><br />
+
+<br />
+
+ <a id="a3" tabindex="1" href="http://home.mozilla.org">This is the 3rd
+
+link but the 1st tabindex</a><br />
+
+<br />
+
+ <a id="a4" tabindex="5" href="http://home.mozilla.org">This is the 4th
+
+link but the 5th tabindex</a><br />
+
+<br />
+
+ <a id="a5" tabindex="2" href="http://home.mozilla.org">This is the 5th
+
+link but the 2nd tabindex</a>
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/bug299673.js b/dom/events/test/bug299673.js
new file mode 100644
index 0000000000..2775355c40
--- /dev/null
+++ b/dom/events/test/bug299673.js
@@ -0,0 +1,150 @@
+var popup;
+
+function OpenWindow() {
+ log({}, ">>> OpenWindow");
+ popup = window.open("", "Test");
+
+ var output = "<html>";
+
+ output += "<body>";
+ output += "<form>";
+ output +=
+ "<input id='popupText1' type='text' onfocus='opener.log(event)' onblur='opener.log(event)'>";
+ output += "</form>";
+ output += "</body>";
+ output += "</html>";
+
+ popup.document.open();
+ popup.document.write(output);
+ popup.document.close();
+
+ popup.document.onclick = function (event) {
+ log(event, "popup-doc");
+ };
+ popup.document.onfocus = function (event) {
+ log(event, "popup-doc");
+ };
+ popup.document.onblur = function (event) {
+ log(event, "popup-doc");
+ };
+ popup.document.onchange = function (event) {
+ log(event, "popup-doc");
+ };
+
+ var e = popup.document.getElementById("popupText1");
+ popup.focus();
+ e.focus();
+ is(
+ popup.document.activeElement,
+ e,
+ "input element in popup should be focused"
+ );
+ log({}, "<<< OpenWindow");
+}
+
+var result;
+
+function log(event, message) {
+ if (event && event.eventPhase == 3) {
+ return;
+ }
+ e = event.currentTarget || event.target || event.srcElement;
+ var id = e ? (e.id ? e.id : e.name ? e.name : e.value ? e.value : "") : "";
+ if (id) {
+ id = "(" + id + ")";
+ }
+ result +=
+ (e ? (e.tagName ? e.tagName : "") : " ") +
+ id +
+ ": " +
+ (event.type ? event.type : "") +
+ " " +
+ (message ? message : "") +
+ "\n";
+}
+
+document.onclick = function (event) {
+ log(event, "top-doc");
+};
+document.onfocus = function (event) {
+ log(event, "top-doc");
+};
+document.onblur = function (event) {
+ log(event, "top-doc");
+};
+document.onchange = function (event) {
+ log(event, "top-doc");
+};
+
+function doTest1_rest2(expectedEventLog, focusAfterCloseId) {
+ try {
+ is(
+ document.activeElement,
+ document.getElementById(focusAfterCloseId),
+ "wrong element is focused after popup was closed"
+ );
+ is(result, expectedEventLog, "unexpected events");
+ SimpleTest.finish();
+ } catch (e) {
+ if (popup) {
+ popup.close();
+ }
+ throw e;
+ }
+}
+function doTest1_rest1(expectedEventLog, focusAfterCloseId) {
+ try {
+ synthesizeKey("V", {}, popup);
+ synthesizeKey("A", {}, popup);
+ synthesizeKey("L", {}, popup);
+ is(
+ popup.document.getElementById("popupText1").value,
+ "VAL",
+ "input element in popup did not accept input"
+ );
+
+ var p = popup;
+ popup = null;
+ p.close();
+
+ SimpleTest.waitForFocus(function () {
+ doTest1_rest2(expectedEventLog, focusAfterCloseId);
+ }, window);
+ } catch (e) {
+ if (popup) {
+ popup.close();
+ }
+ throw e;
+ }
+}
+
+function doTest1(expectedEventLog, focusAfterCloseId) {
+ try {
+ var select1 = document.getElementById("Select1");
+ select1.focus();
+ is(document.activeElement, select1, "select element should be focused");
+ synthesizeKey("KEY_ArrowDown");
+ synthesizeKey("KEY_Tab");
+ SimpleTest.waitForFocus(function () {
+ doTest1_rest1(expectedEventLog, focusAfterCloseId);
+ }, popup);
+ } catch (e) {
+ if (popup) {
+ popup.close();
+ }
+ throw e;
+ }
+}
+
+function setPrefAndDoTest(expectedEventLog, focusAfterCloseId, prefValue) {
+ var select1 = document.getElementById("Select1");
+ select1.blur();
+ result = "";
+ log({}, "Test with browser.link.open_newwindow = " + prefValue);
+ SpecialPowers.pushPrefEnv(
+ { set: [["browser.link.open_newwindow", prefValue]] },
+ function () {
+ doTest1(expectedEventLog, focusAfterCloseId);
+ }
+ );
+}
diff --git a/dom/events/test/bug322588-popup.html b/dom/events/test/bug322588-popup.html
new file mode 100644
index 0000000000..767eb9db9c
--- /dev/null
+++ b/dom/events/test/bug322588-popup.html
@@ -0,0 +1 @@
+<html><head></head><body onblur="window.close()"><a id="target">a id=target</a></body></html>
diff --git a/dom/events/test/bug415498-doc1.html b/dom/events/test/bug415498-doc1.html
new file mode 100644
index 0000000000..e8fbca6c9a
--- /dev/null
+++ b/dom/events/test/bug415498-doc1.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">
+function init() {
+ // This will throw a HierarchyRequestError exception
+ var doc = document.implementation.createDocument(null, 'DOC', null);
+ doc.documentElement.appendChild(doc);
+}
+window.addEventListener("load", init);
+</script>
+</head>
+<body>
+ Testcase for bug 415498. This page should show an exception in Error Console on load
+</body>
diff --git a/dom/events/test/bug415498-doc2.html b/dom/events/test/bug415498-doc2.html
new file mode 100644
index 0000000000..e556a4e4ca
--- /dev/null
+++ b/dom/events/test/bug415498-doc2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<script type="text/javascript">
+function init() {
+ // This will throw a HierarchyRequestError exception
+ var doc = document.implementation.createDocument(null, 'DOC', null);
+ doc.documentElement.appendChild(doc);
+}
+onload = init;
+</script>
+</head>
+<body>
+ Testcase for bug 415498. This page should show an exception in Error Console on load
+</body>
diff --git a/dom/events/test/bug418986-3.js b/dom/events/test/bug418986-3.js
new file mode 100644
index 0000000000..81d04b9b22
--- /dev/null
+++ b/dom/events/test/bug418986-3.js
@@ -0,0 +1,85 @@
+SimpleTest.waitForExplicitFinish();
+
+// The main testing function.
+var test = async function (isContent) {
+ await SpecialPowers.contentTransformsReceived(window);
+
+ // Each definition is [eventType, prefSetting]
+ // Where we are setting the "privacy.resistFingerprinting" pref.
+ let eventDefs = [
+ ["mousedown", true],
+ ["mouseup", true],
+ ["mousedown", false],
+ ["mouseup", false],
+ ];
+
+ let testCounter = 0;
+
+ // Declare ahead of time.
+ let setup;
+
+ // This function is called when the event handler fires.
+ let handleEvent = function (event, prefVal) {
+ let resisting = prefVal && isContent;
+ if (resisting) {
+ is(
+ event.screenX,
+ event.clientX,
+ "event.screenX and event.clientX should be the same"
+ );
+ is(
+ event.screenY,
+ event.clientY,
+ "event.screenY and event.clientY should be the same"
+ );
+ } else {
+ // We can't be sure about X coordinates not being equal, but we can test Y.
+ isnot(event.screenY, event.clientY, "event.screenY !== event.clientY");
+ }
+ ++testCounter;
+ if (testCounter < eventDefs.length) {
+ nextTest();
+ } else {
+ SimpleTest.finish();
+ }
+ };
+
+ // In this function, we set up the nth div and event handler,
+ // and then synthesize a mouse event in the div, to test
+ // whether the resulting events resist fingerprinting by
+ // suppressing absolute screen coordinates.
+ nextTest = function () {
+ let [eventType, prefVal] = eventDefs[testCounter];
+ SpecialPowers.pushPrefEnv(
+ { set: [["privacy.resistFingerprinting", prefVal]] },
+ function () {
+ // The following code creates a new div for each event in eventDefs,
+ // attaches a listener to listen for the event, and then generates
+ // a fake event at the center of the div.
+ let div = document.createElementNS(
+ "http://www.w3.org/1999/xhtml",
+ "div"
+ );
+ div.style.width = "10px";
+ div.style.height = "10px";
+ div.style.backgroundColor = "red";
+ // Name the div after the event we're listening for.
+ div.id = eventType;
+ document.getElementById("body").appendChild(div);
+ // Seems we can't add an event listener in chrome unless we run
+ // it in a later task.
+ window.setTimeout(function () {
+ div.addEventListener(eventType, event => handleEvent(event, prefVal));
+ // For some reason, the following synthesizeMouseAtCenter call only seems to run if we
+ // wrap it in a window.setTimeout(..., 0).
+ window.setTimeout(function () {
+ synthesizeMouseAtCenter(div, { type: eventType });
+ }, 0);
+ }, 0);
+ }
+ );
+ };
+
+ // Now run by starting with the 0th event.
+ nextTest();
+};
diff --git a/dom/events/test/bug426082.html b/dom/events/test/bug426082.html
new file mode 100644
index 0000000000..b8bf5cb243
--- /dev/null
+++ b/dom/events/test/bug426082.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=426082
+-->
+<head>
+ <title>Test for Bug 426082</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=426082">Mozilla Bug 426082</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<p><input type="button" value="Button" id="button"></p>
+<p><label for="button" id="label">Label</label></p>
+<p id="outside">Something under the label</p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 426082 **/
+
+function runTests() {
+ SimpleTest.executeSoon(tests);
+}
+
+SimpleTest.waitForFocus(runTests);
+
+function oneTick() {
+ return new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve)));
+}
+
+function sendMouseEvent(t, elem) {
+ let r = elem.getBoundingClientRect();
+ synthesizeMouse(elem, r.width / 2, r.height / 2, {type: t});
+}
+
+async function tests() {
+ let button = document.getElementById("button");
+ let label = document.getElementById("label");
+ let outside = document.getElementById("outside");
+
+ let is = window.opener.is;
+ let ok = window.opener.ok;
+
+ // Press the label.
+ sendMouseEvent("mousemove", label);
+ sendMouseEvent("mousedown", label);
+
+ await oneTick();
+
+ ok(label.matches(":hover"), "Label is hovered");
+ ok(button.matches(":hover"), "Button should be hovered too");
+
+ ok(label.matches(":active"), "Label is active");
+ ok(button.matches(":active"), "Button should be active too");
+
+ // Move the mouse down from the label.
+ sendMouseEvent("mousemove", outside);
+
+ await oneTick();
+
+ ok(!label.matches(":hover"), "Label is no longer hovered");
+ ok(!button.matches(":hover"), "Button should not be hovered too");
+
+ ok(label.matches(":active"), "Label is still active");
+ ok(button.matches(":active"), "Button is still active too");
+
+ // And up again.
+ sendMouseEvent("mousemove", label);
+
+ await oneTick();
+
+
+ ok(label.matches(":hover"), "Label hovered again");
+ ok(button.matches(":hover"), "Button be hovered again");
+ ok(label.matches(":active"), "Label is still active");
+ ok(button.matches(":active"), "Button is still active too");
+
+ // Release.
+ sendMouseEvent("mouseup", label);
+
+ await oneTick();
+
+ ok(!label.matches(":active"), "Label is no longer active");
+ ok(!button.matches(":active"), "Button is no longer active");
+
+ ok(label.matches(":hover"), "Label is still hovered");
+ ok(button.matches(":hover"), "Button is still hovered");
+
+ // Press the label and remove it.
+ sendMouseEvent("mousemove", label);
+ sendMouseEvent("mousedown", label);
+
+ await oneTick();
+
+ label.remove();
+
+ await oneTick();
+
+ ok(!label.matches(":active"), "Removing label should have unpressed it");
+ ok(!label.matches(":focus"), "Removing label should have unpressed it");
+ ok(!label.matches(":hover"), "Removing label should have unhovered it");
+ ok(!button.matches(":active"), "Removing label should have unpressed the button");
+ ok(!button.matches(":focus"), "Removing label should have unpressed the button");
+ ok(!button.matches(":hover"), "Removing label should have unhovered the button");
+
+ sendMouseEvent("mouseup", label);
+ window.opener.finishTests();
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/bug545268.html b/dom/events/test/bug545268.html
new file mode 100644
index 0000000000..1f90149a54
--- /dev/null
+++ b/dom/events/test/bug545268.html
@@ -0,0 +1 @@
+<iframe id='f' style='position:absolute; border:none; width:100%; height:100%; left:0; top:0' srcdoc='<input>'>
diff --git a/dom/events/test/bug574663.html b/dom/events/test/bug574663.html
new file mode 100644
index 0000000000..a3a5bd30ba
--- /dev/null
+++ b/dom/events/test/bug574663.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<div id="scrollbox" style="height: 100px; overflow: auto;">
+<div style="height: 1000px;"></div></div>
diff --git a/dom/events/test/bug591249_iframe.xhtml b/dom/events/test/bug591249_iframe.xhtml
new file mode 100644
index 0000000000..7c7d7642b1
--- /dev/null
+++ b/dom/events/test/bug591249_iframe.xhtml
@@ -0,0 +1,33 @@
+<?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=591249
+-->
+<window title="Mozilla Bug 591249"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <html:style type="text/css">
+ #drop-target {
+ width: 50px;
+ height: 50px;
+ border: 4px dotted black;
+ }
+ #drop-target {
+ background-color: red;
+ }
+ #drop-target:-moz-drag-over {
+ background-color: yellow;
+ }
+ </html:style>
+
+ <html:body>
+ <html:h1 id="iframetext">Iframe for Bug 591249</html:h1>
+
+ <html:div id="drop-target"
+ ondrop="return false;"
+ ondragenter="return false;"
+ ondragover="return false;">
+ </html:div>
+ </html:body>
+</window>
diff --git a/dom/events/test/bug602962.xhtml b/dom/events/test/bug602962.xhtml
new file mode 100644
index 0000000000..43affd6b60
--- /dev/null
+++ b/dom/events/test/bug602962.xhtml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window onload="window.opener.doTest()" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <scrollbox id="page-scrollbox" style="border: 1px solid red; background-color: black;overflow: auto" flex="1">
+ <box id="page-box" style="flex-shrink: 0; border: 1px solid green;"/>
+ </scrollbox>
+</window>
diff --git a/dom/events/test/bug607464.html b/dom/events/test/bug607464.html
new file mode 100644
index 0000000000..55d1152623
--- /dev/null
+++ b/dom/events/test/bug607464.html
@@ -0,0 +1,3 @@
+<!DOCTYPE html>
+<div id="scrollbox" style="height: 100px; overflow: auto;">
+<div style="height: 1000px;"></div> </div>
diff --git a/dom/events/test/bug656379-1.html b/dom/events/test/bug656379-1.html
new file mode 100644
index 0000000000..169d40a7c2
--- /dev/null
+++ b/dom/events/test/bug656379-1.html
@@ -0,0 +1,186 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=656379
+-->
+<head>
+ <title>Test for Bug 656379</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>
+ canvas {
+ display: none;
+ }
+ input[type=button] {
+ appearance: none;
+ padding: 0;
+ border: none;
+ color: black;
+ background: white;
+ }
+ input[type=button]::-moz-focus-inner { border: none; }
+
+ /* Make sure that normal, focused, hover+active, focused+hover+active
+ buttons all have different styles so that the test keeps moving along. */
+ input[type=button]:hover:active {
+ background: red;
+ }
+ input[type=button]:focus {
+ background: green;
+ }
+ input[type=button]:focus:hover:active {
+ background: purple;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=656379">Mozilla Bug 656379</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+
+var normalButtonCanvas, pressedButtonCanvas, normalFocusedButtonCanvas,
+ pressedFocusedButtonCanvas, currentSnapshot, button, label, outside;
+
+function runTests() {
+ button = $("button");
+ label = $("label");
+ outside = $("outside");
+ SimpleTest.executeSoon(executeTests);
+}
+
+SimpleTest.waitForFocus(runTests);
+
+function isRectContainedInRectFromRegion(rect, region) {
+ return Array.prototype.some.call(region, function (r) {
+ return rect.left >= r.left &&
+ rect.top >= r.top &&
+ rect.right <= r.right &&
+ rect.bottom <= r.bottom;
+ });
+}
+
+function paintListener(e) {
+ if (isRectContainedInRectFromRegion(buttonRect(), SpecialPowers.wrap(e).clientRects)) {
+ gNeedsPaint = false;
+ currentSnapshot = takeSnapshot();
+ }
+}
+
+var gNeedsPaint = false;
+function executeTests() {
+ var testYielder = tests();
+ function execNext() {
+ if (!gNeedsPaint) {
+ let {done} = testYielder.next();
+ if (done) {
+ return;
+ }
+ button.getBoundingClientRect(); // Flush.
+ gNeedsPaint = true;
+ }
+ SimpleTest.executeSoon(execNext);
+ }
+ execNext();
+}
+
+function* tests() {
+ window.addEventListener("MozAfterPaint", paintListener);
+ normalButtonCanvas = takeSnapshot();
+ // Press the button.
+ sendMouseEvent("mousemove", button);
+ sendMouseEvent("mousedown", button);
+ yield undefined;
+ pressedFocusedButtonCanvas = takeSnapshot();
+ compareSnapshots_(normalButtonCanvas, pressedFocusedButtonCanvas, false, "Pressed focused buttons should look different from normal buttons.");
+ // Release.
+ sendMouseEvent("mouseup", button);
+ yield undefined;
+ // make sure the button is focused as this doesn't happen on click on Mac
+ button.focus();
+ normalFocusedButtonCanvas = takeSnapshot();
+ compareSnapshots_(normalFocusedButtonCanvas, pressedFocusedButtonCanvas, false, "Pressed focused buttons should look different from normal focused buttons.");
+ // Unfocus the button.
+ sendMouseEvent("mousedown", outside);
+ sendMouseEvent("mouseup", outside);
+ yield undefined;
+
+ // Press the label.
+ sendMouseEvent("mousemove", label);
+ sendMouseEvent("mousedown", label);
+ yield undefined;
+ compareSnapshots_(normalButtonCanvas, currentSnapshot, false, "Pressing the label should have pressed the button.");
+ pressedButtonCanvas = takeSnapshot();
+ // Move the mouse down from the label.
+ sendMouseEvent("mousemove", outside);
+ yield undefined;
+ compareSnapshots_(normalButtonCanvas, currentSnapshot, true, "Moving the mouse down from the label should have unpressed the button.");
+ // ... and up again.
+ sendMouseEvent("mousemove", label);
+ yield undefined;
+ compareSnapshots_(pressedButtonCanvas, currentSnapshot, true, "Moving the mouse back on top of the label should have pressed the button.");
+ // Release.
+ sendMouseEvent("mouseup", label);
+ yield undefined;
+ var focusOnMouse = SpecialPowers.getIntPref("accessibility.mouse_focuses_formcontrol") > 0;
+ compareSnapshots_(focusOnMouse ? normalFocusedButtonCanvas : normalButtonCanvas,
+ currentSnapshot, true, "Releasing the mouse over the label should have unpressed" +
+ (focusOnMouse ? " (and focused)" : "") + " the button.");
+ // Press the label and remove it.
+ sendMouseEvent("mousemove", label);
+ sendMouseEvent("mousedown", label);
+ yield undefined;
+ label.remove();
+ yield undefined;
+ compareSnapshots_(normalButtonCanvas, currentSnapshot, true, "Removing the label should have unpressed the button.");
+ sendMouseEvent("mouseup", label);
+ window.removeEventListener("MozAfterPaint", paintListener);
+ window.opener.finishTests();
+ }
+
+function sendMouseEvent(t, elem) {
+ var r = elem.getBoundingClientRect();
+ synthesizeMouse(elem, r.width / 2, r.height / 2, {type: t});
+}
+
+function compareSnapshots_(c1, c2, shouldBeIdentical, msg) {
+ var [correct, c1url, c2url] = compareSnapshots(c1, c2, shouldBeIdentical);
+ if (correct) {
+ if (shouldBeIdentical) {
+ window.opener.ok(true, msg + " - expected " + c1url);
+ } else {
+ window.opener.ok(true, msg + " - got " + c1url + " and " + c2url);
+ }
+ } else {
+ if (shouldBeIdentical) {
+ window.opener.ok(false, msg + " - expected " + c1url + " but got " + c2url);
+ } else {
+ window.opener.ok(false, msg + " - expected something other than " + c1url);
+ }
+ }
+}
+
+function takeSnapshot(canvas) {
+ var r = buttonRect();
+ adjustedRect = { left: r.left - 2, top: r.top - 2,
+ width: r.width + 4, height: r.height + 4 };
+ return SpecialPowers.snapshotRect(window, adjustedRect);
+}
+
+function buttonRect() {
+ return button.getBoundingClientRect();
+}
+</script>
+</pre>
+<p><input type="button" value="Button" id="button"></p>
+<p><label for="button" id="label">Label</label></p>
+<p id="outside">Something under the label</p>
+
+</body>
+</html>
diff --git a/dom/events/test/chrome.toml b/dom/events/test/chrome.toml
new file mode 100644
index 0000000000..4a1221d664
--- /dev/null
+++ b/dom/events/test/chrome.toml
@@ -0,0 +1,48 @@
+[DEFAULT]
+skip-if = ["os == 'android'"]
+support-files = [
+ "bug415498-doc1.html",
+ "bug415498-doc2.html",
+ "bug418986-3.js",
+ "bug591249_iframe.xhtml",
+ "bug602962.xhtml",
+ "file_bug679494.html",
+ "window_bug617528.xhtml",
+ "window_bug1412775.xhtml",
+ "test_bug336682.js",
+]
+
+["test_DataTransferItemList.html"]
+skip-if = ["!debug && (os == 'linux')"] #Bug 1421150
+
+["test_bug336682_2.xhtml"]
+
+["test_bug415498.xhtml"]
+
+["test_bug418986-3.xhtml"]
+
+["test_bug524674.xhtml"]
+
+["test_bug547996-3.xhtml"]
+
+["test_bug586961.xhtml"]
+
+["test_bug591249.xhtml"]
+
+["test_bug602962.xhtml"]
+
+["test_bug617528.xhtml"]
+
+["test_bug679494.xhtml"]
+
+["test_bug930374-chrome.html"]
+
+["test_bug1128787-1.html"]
+
+["test_bug1128787-2.html"]
+
+["test_bug1128787-3.html"]
+
+["test_bug1412775.xhtml"]
+
+["test_eventctors.xhtml"]
diff --git a/dom/events/test/clipboard/browser.toml b/dom/events/test/clipboard/browser.toml
new file mode 100644
index 0000000000..8ced9423dd
--- /dev/null
+++ b/dom/events/test/clipboard/browser.toml
@@ -0,0 +1,40 @@
+[DEFAULT]
+support-files = [
+ "head.js",
+ "!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+ "!/gfx/layers/apz/test/mochitest/apz_test_utils.js",
+]
+
+["browser_navigator_clipboard_clickjacking.js"]
+skip-if = [
+ "os == 'win'", # The popupmenus dismiss when access keys for disabled items are pressed on windows
+ "os == 'mac' && verify",
+]
+support-files = ["simple_navigator_clipboard_keydown.html"]
+
+["browser_navigator_clipboard_contextmenu_suppression.js"]
+support-files = [
+ "file_toplevel.html",
+ "file_iframe.html",
+]
+
+["browser_navigator_clipboard_contextmenu_suppression_ext.js"]
+support-files = [
+ "file_toplevel.html",
+ "file_iframe.html",
+]
+
+["browser_navigator_clipboard_read.js"]
+support-files = ["simple_navigator_clipboard_read.html"]
+
+["browser_navigator_clipboard_readText.js"]
+support-files = ["simple_navigator_clipboard_readText.html"]
+
+["browser_navigator_clipboard_readText_multiple.js"]
+support-files = [
+ "file_toplevel.html",
+ "file_iframe.html",
+]
+
+["browser_navigator_clipboard_touch.js"]
+support-files = ["simple_navigator_clipboard_readText.html"]
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_clickjacking.js b/dom/events/test/clipboard/browser_navigator_clipboard_clickjacking.js
new file mode 100644
index 0000000000..cd3e97f274
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_clickjacking.js
@@ -0,0 +1,69 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+const kContentFileName = "simple_navigator_clipboard_keydown.html";
+
+const kContentFileUrl = kBaseUrlForContent + kContentFileName;
+
+const kApzTestNativeEventUtilsUrl =
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js";
+
+Services.scriptloader.loadSubScript(kApzTestNativeEventUtilsUrl, this);
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.events.asyncClipboard.readText", true]],
+ });
+});
+
+add_task(async function test_paste_button_clickjacking() {
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+
+ // synthesize key to trigger readText() to bring up paste popup.
+ EventUtils.synthesizeKey("p", {}, window);
+ await waitForPasteMenuPopupEvent("shown");
+
+ const pastePopup = document.getElementById(kPasteMenuPopupId);
+ const pasteButton = document.getElementById(kPasteMenuItemId);
+ ok(
+ pasteButton.disabled,
+ "Paste button should be shown with disabled by default"
+ );
+
+ let accesskey = pasteButton.getAttribute("accesskey");
+ let delay = Services.prefs.getIntPref("security.dialog_enable_delay") * 3;
+ while (delay > 0) {
+ // There's no other way to allow some time to pass and ensure we're
+ // genuinely testing that these keypresses postpone the enabling of
+ // the paste button, so disable this check for this line:
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(r => setTimeout(r, 100));
+ ok(pasteButton.disabled, "Paste button should still be disabled");
+ EventUtils.synthesizeKey(accesskey, {}, window);
+ is(pastePopup.state, "open", "Paste popup should still be opened");
+ delay = delay - 100;
+ }
+
+ await BrowserTestUtils.waitForMutationCondition(
+ pasteButton,
+ { attributeFilter: ["disabled"] },
+ () => !pasteButton.disabled,
+ "Wait for paste button enabled"
+ );
+
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ EventUtils.synthesizeKey(accesskey, {}, window);
+ await pasteButtonIsHidden;
+ });
+});
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js
new file mode 100644
index 0000000000..f504e499c9
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression.js
@@ -0,0 +1,264 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+requestLongerTimeout(2);
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const kContentFileName = "file_toplevel.html";
+const kContentFileUrl = kBaseUrlForContent + kContentFileName;
+const kIsMac = navigator.platform.indexOf("Mac") > -1;
+
+async function waitForPasteContextMenu() {
+ await waitForPasteMenuPopupEvent("shown");
+ let pasteButton = document.getElementById(kPasteMenuItemId);
+ info("Wait for paste button enabled");
+ await BrowserTestUtils.waitForMutationCondition(
+ pasteButton,
+ { attributeFilter: ["disabled"] },
+ () => !pasteButton.disabled,
+ "Wait for paste button enabled"
+ );
+}
+
+async function readText(aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+}
+
+function testPasteContextMenuSuppression(aWriteFun, aMsg) {
+ add_task(async function test_context_menu_suppression_sameorigin() {
+ await BrowserTestUtils.withNewTab(
+ kContentFileUrl,
+ async function (browser) {
+ info(`Write data by ${aMsg}`);
+ let clipboardText = await aWriteFun(browser);
+
+ info("Test read from same-origin frame");
+ let listener = function (e) {
+ if (e.target.getAttribute("id") == kPasteMenuPopupId) {
+ ok(false, "paste contextmenu should not be shown");
+ }
+ };
+ document.addEventListener("popupshown", listener);
+ is(
+ await readText(browser.browsingContext.children[0]),
+ clipboardText,
+ "read should just be resolved without paste contextmenu shown"
+ );
+ document.removeEventListener("popupshown", listener);
+ }
+ );
+ });
+
+ add_task(async function test_context_menu_suppression_crossorigin() {
+ await BrowserTestUtils.withNewTab(
+ kContentFileUrl,
+ async function (browser) {
+ info(`Write data by ${aMsg}`);
+ let clipboardText = await aWriteFun(browser);
+
+ info("Test read from cross-origin frame");
+ let pasteButtonIsShown = waitForPasteContextMenu();
+ let readTextRequest = readText(browser.browsingContext.children[1]);
+ await pasteButtonIsShown;
+
+ info("Click paste button, request should be resolved");
+ await promiseClickPasteButton();
+ is(await readTextRequest, clipboardText, "Request should be resolved");
+ }
+ );
+ });
+
+ add_task(async function test_context_menu_suppression_multiple() {
+ await BrowserTestUtils.withNewTab(
+ kContentFileUrl,
+ async function (browser) {
+ info(`Write data by ${aMsg}`);
+ let clipboardText = await aWriteFun(browser);
+
+ info("Test read from cross-origin frame");
+ let pasteButtonIsShown = waitForPasteContextMenu();
+ let readTextRequest1 = readText(browser.browsingContext.children[1]);
+ await pasteButtonIsShown;
+
+ info(
+ "Test read from same-origin frame before paste contextmenu is closed"
+ );
+ is(
+ await readText(browser.browsingContext.children[0]),
+ clipboardText,
+ "read from same-origin should just be resolved without showing paste contextmenu shown"
+ );
+
+ info("Dismiss paste button, cross-origin request should be rejected");
+ await promiseDismissPasteButton();
+ await Assert.rejects(
+ readTextRequest1,
+ /NotAllowedError/,
+ "cross-origin request should be rejected"
+ );
+ }
+ );
+ });
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.readText", true],
+ ["dom.events.asyncClipboard.clipboardItem", true],
+ ["test.events.async.enabled", true],
+ // Avoid paste button delay enabling making test too long.
+ ["security.dialog_enable_delay", 0],
+ ],
+ });
+});
+
+testPasteContextMenuSuppression(async aBrowser => {
+ const clipboardText = "X" + Math.random();
+ await SpecialPowers.spawn(aBrowser, [clipboardText], async text => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.writeText("${text}");`);
+ });
+ return clipboardText;
+}, "clipboard.writeText()");
+
+testPasteContextMenuSuppression(async aBrowser => {
+ const clipboardText = "X" + Math.random();
+ await SpecialPowers.spawn(aBrowser, [clipboardText], async text => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`
+ const itemInput = new ClipboardItem({["text/plain"]: "${text}"});
+ navigator.clipboard.write([itemInput]);
+ `);
+ });
+ return clipboardText;
+}, "clipboard.write()");
+
+testPasteContextMenuSuppression(async aBrowser => {
+ const clipboardText = "X" + Math.random();
+ await SpecialPowers.spawn(aBrowser, [clipboardText], async text => {
+ let div = content.document.createElement("div");
+ div.innerText = text;
+ content.document.documentElement.appendChild(div);
+ // select text
+ content
+ .getSelection()
+ .setBaseAndExtent(div.firstChild, text.length, div.firstChild, 0);
+ });
+ // trigger keyboard shortcut to copy.
+ await EventUtils.synthesizeAndWaitKey(
+ "c",
+ kIsMac ? { accelKey: true } : { ctrlKey: true }
+ );
+ return clipboardText;
+}, "keyboard shortcut");
+
+testPasteContextMenuSuppression(async aBrowser => {
+ const clipboardText = "X" + Math.random();
+ await SpecialPowers.spawn(aBrowser, [clipboardText], async text => {
+ return content.eval(`
+ document.addEventListener("copy", function(e) {
+ e.preventDefault();
+ e.clipboardData.setData("text/plain", "${text}");
+ }, { once: true });
+ `);
+ });
+ // trigger keyboard shortcut to copy.
+ await EventUtils.synthesizeAndWaitKey(
+ "c",
+ kIsMac ? { accelKey: true } : { ctrlKey: true }
+ );
+ return clipboardText;
+}, "keyboard shortcut with custom data");
+
+testPasteContextMenuSuppression(async aBrowser => {
+ const clipboardText = "X" + Math.random();
+ await SpecialPowers.spawn(aBrowser, [clipboardText], async text => {
+ let div = content.document.createElement("div");
+ div.innerText = text;
+ content.document.documentElement.appendChild(div);
+ // select text
+ content
+ .getSelection()
+ .setBaseAndExtent(div.firstChild, text.length, div.firstChild, 0);
+ return SpecialPowers.doCommand(content, "cmd_copy");
+ });
+ return clipboardText;
+}, "copy command");
+
+async function readTypes(aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ let items = await content.eval(`navigator.clipboard.read();`);
+ return items[0].types;
+ });
+}
+
+add_task(async function test_context_menu_suppression_image() {
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ await SpecialPowers.spawn(browser, [], async () => {
+ let image = content.document.createElement("img");
+ let copyImagePromise = new Promise(resolve => {
+ image.addEventListener(
+ "load",
+ e => {
+ let documentViewer = content.docShell.docViewer.QueryInterface(
+ SpecialPowers.Ci.nsIDocumentViewerEdit
+ );
+ documentViewer.setCommandNode(image);
+ documentViewer.copyImage(documentViewer.COPY_IMAGE_ALL);
+ resolve();
+ },
+ { once: true }
+ );
+ });
+ image.src =
+ "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABHCAIAAADQjmMaAA" +
+ "AACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3goUAwAgSAORBwAAABl0RVh0Q29tbW" +
+ "VudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAABPSURBVGje7c4BDQAACAOga//OmuMbJG" +
+ "AurTbq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6u" +
+ "rq6s31B0IqAY2/tQVCAAAAAElFTkSuQmCC";
+ content.document.documentElement.appendChild(image);
+ await copyImagePromise;
+ });
+
+ info("Test read from cross-origin frame");
+ let pasteButtonIsShown = waitForPasteContextMenu();
+ let readTypesRequest1 = readTypes(browser.browsingContext.children[1]);
+ await pasteButtonIsShown;
+
+ info("Test read from same-origin frame before paste contextmenu is closed");
+ const clipboarCacheEnabled = SpecialPowers.getBoolPref(
+ "widget.clipboard.use-cached-data.enabled",
+ false
+ );
+ // If the cached data is used, it uses type order in cached transferable.
+ SimpleTest.isDeeply(
+ await readTypes(browser.browsingContext.children[0]),
+ clipboarCacheEnabled
+ ? ["text/plain", "text/html", "image/png"]
+ : ["text/html", "text/plain", "image/png"],
+ "read from same-origin should just be resolved without showing paste contextmenu shown"
+ );
+
+ info("Dismiss paste button, cross-origin request should be rejected");
+ await promiseDismissPasteButton();
+ // XXX edgar: not sure why first promiseDismissPasteButton doesn't work on Windows opt build.
+ await promiseDismissPasteButton();
+ await Assert.rejects(
+ readTypesRequest1,
+ /NotAllowedError/,
+ "cross-origin request should be rejected"
+ );
+ });
+});
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression_ext.js b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression_ext.js
new file mode 100644
index 0000000000..1990db05ec
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_contextmenu_suppression_ext.js
@@ -0,0 +1,156 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+requestLongerTimeout(2);
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const kContentFileName = "file_toplevel.html";
+const kContentFileUrl = kBaseUrlForContent + kContentFileName;
+const kIsMac = navigator.platform.indexOf("Mac") > -1;
+
+async function waitForPasteContextMenu() {
+ await waitForPasteMenuPopupEvent("shown");
+ let pasteButton = document.getElementById(kPasteMenuItemId);
+ info("Wait for paste button enabled");
+ await BrowserTestUtils.waitForMutationCondition(
+ pasteButton,
+ { attributeFilter: ["disabled"] },
+ () => !pasteButton.disabled,
+ "Wait for paste button enabled"
+ );
+}
+
+async function readText(aBrowser) {
+ return SpecialPowers.spawn(aBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+}
+
+async function testPasteContextMenu(
+ aBrowser,
+ aClipboardText,
+ aShouldShow = true
+) {
+ let pasteButtonIsShown;
+ if (aShouldShow) {
+ pasteButtonIsShown = waitForPasteContextMenu();
+ }
+ let readTextRequest = readText(aBrowser);
+ if (aShouldShow) {
+ await pasteButtonIsShown;
+ }
+
+ info("Click paste button, request should be resolved");
+ if (aShouldShow) {
+ await promiseClickPasteButton();
+ }
+ is(await readTextRequest, aClipboardText, "Request should be resolved");
+}
+
+async function installAndStartExtension(aContentScript) {
+ let extension = ExtensionTestUtils.loadExtension({
+ manifest: {
+ content_scripts: [
+ {
+ js: ["content_script.js"],
+ matches: ["https://example.com/*/file_toplevel.html"],
+ },
+ ],
+ },
+ files: {
+ "content_script.js": aContentScript,
+ },
+ });
+
+ await extension.startup();
+
+ return extension;
+}
+
+function testExtensionContentScript(aContentScript, aMessage) {
+ add_task(async function test_context_menu_suppression_ext() {
+ info(`${aMessage}`);
+ const extension = await installAndStartExtension(aContentScript);
+ await BrowserTestUtils.withNewTab(
+ kContentFileUrl,
+ async function (browser) {
+ const clipboardText = "X" + Math.random();
+ await SpecialPowers.spawn(browser, [clipboardText], async text => {
+ info(`Set clipboard text to ${text}`);
+ let div = content.document.createElement("div");
+ div.id = "container";
+ div.innerText = text;
+ content.document.documentElement.appendChild(div);
+ });
+
+ let writePromise = extension.awaitMessage("write-data-ready");
+ // trigger keyboard shortcut to copy.
+ await EventUtils.synthesizeAndWaitKey(
+ "c",
+ kIsMac ? { accelKey: true } : { ctrlKey: true }
+ );
+ // Wait a bit for clipboard write.
+ await writePromise;
+
+ info("Test read from same frame");
+ await testPasteContextMenu(browser, clipboardText, false);
+
+ info("Test read from same-origin subframe");
+ await testPasteContextMenu(
+ browser.browsingContext.children[0],
+ clipboardText,
+ false
+ );
+
+ info("Test read from cross-origin subframe");
+ await testPasteContextMenu(
+ browser.browsingContext.children[1],
+ clipboardText,
+ true
+ );
+ }
+ );
+
+ await extension.unload();
+ });
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.readText", true],
+ ["dom.events.asyncClipboard.clipboardItem", true],
+ ["test.events.async.enabled", true],
+ // Avoid paste button delay enabling making test too long.
+ ["security.dialog_enable_delay", 0],
+ ],
+ });
+});
+
+testExtensionContentScript(() => {
+ document.addEventListener("copy", function (e) {
+ e.preventDefault();
+ let div = document.getElementById("container");
+ let text = div.innerText;
+ e.clipboardData.setData("text/plain", text);
+ browser.test.sendMessage("write-data-ready");
+ });
+}, "Write data by DataTransfer API in extension");
+
+testExtensionContentScript(() => {
+ document.addEventListener("copy", async function (e) {
+ e.preventDefault();
+ let div = document.getElementById("container");
+ let text = div.innerText;
+ await navigator.clipboard.writeText(text);
+ browser.test.sendMessage("write-data-ready");
+ });
+}, "Write data by Async Clipboard API in extension");
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_read.js b/dom/events/test/clipboard/browser_navigator_clipboard_read.js
new file mode 100644
index 0000000000..e11002e7f8
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_read.js
@@ -0,0 +1,228 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+const kContentFileName = "simple_navigator_clipboard_read.html";
+
+const kContentFileUrl = kBaseUrlForContent + kContentFileName;
+
+const kApzTestNativeEventUtilsUrl =
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js";
+
+Services.scriptloader.loadSubScript(kApzTestNativeEventUtilsUrl, this);
+
+// @param aBrowser browser object of the content tab.
+// @param aMultipleReadTextCalls if false, exactly one call is made, two
+// otherwise.
+function promiseClickContentToTriggerClipboardRead(
+ aBrowser,
+ aMultipleReadTextCalls
+) {
+ return promiseClickContentElement(
+ aBrowser,
+ aMultipleReadTextCalls ? "invokeReadTwiceId" : "invokeReadOnceId"
+ );
+}
+
+// @param aBrowser browser object of the content tab.
+function promiseMutatedReadResultFromContentElement(aBrowser) {
+ return promiseMutatedTextContentFromContentElement(aBrowser, "readResultId");
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.clipboardItem", true],
+ ["test.events.async.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test_paste_button_position() {
+ // Ensure there's text on the clipboard.
+ await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false });
+ const coordsOfClickInContentRelativeToScreenInDevicePixels =
+ await promiseClickContentToTriggerClipboardRead(browser, false);
+ AccessibilityUtils.resetEnv();
+ info(
+ "coordsOfClickInContentRelativeToScreenInDevicePixels: " +
+ coordsOfClickInContentRelativeToScreenInDevicePixels.x +
+ ", " +
+ coordsOfClickInContentRelativeToScreenInDevicePixels.y
+ );
+
+ const pasteButtonCoordsRelativeToScreenInDevicePixels =
+ await pasteButtonIsShown;
+ info(
+ "pasteButtonCoordsRelativeToScreenInDevicePixels: " +
+ pasteButtonCoordsRelativeToScreenInDevicePixels.x +
+ ", " +
+ pasteButtonCoordsRelativeToScreenInDevicePixels.y
+ );
+
+ const mouseCoordsRelativeToScreenInDevicePixels =
+ getMouseCoordsRelativeToScreenInDevicePixels();
+ info(
+ "mouseCoordsRelativeToScreenInDevicePixels: " +
+ mouseCoordsRelativeToScreenInDevicePixels.x +
+ ", " +
+ mouseCoordsRelativeToScreenInDevicePixels.y
+ );
+
+ // Asserting not overlapping is important; otherwise, when the
+ // "Paste" button is shown via a `mousedown` event, the following
+ // `mouseup` event could accept the "Paste" button unnoticed by the
+ // user.
+ ok(
+ isCloselyLeftOnTopOf(
+ mouseCoordsRelativeToScreenInDevicePixels,
+ pasteButtonCoordsRelativeToScreenInDevicePixels
+ ),
+ "'Paste' button is closely left on top of the mouse pointer."
+ );
+ ok(
+ isCloselyLeftOnTopOf(
+ coordsOfClickInContentRelativeToScreenInDevicePixels,
+ pasteButtonCoordsRelativeToScreenInDevicePixels
+ ),
+ "Coords of click in content are closely left on top of the 'Paste' button."
+ );
+
+ // To avoid disturbing subsequent tests.
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ });
+});
+
+add_task(async function test_accepting_paste_button() {
+ // Randomized text to avoid overlappings with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false });
+ await promiseClickContentToTriggerClipboardRead(browser, false);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ const mutatedReadResultFromContentElement =
+ promiseMutatedReadResultFromContentElement(browser);
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ await mutatedReadResultFromContentElement.then(value => {
+ is(
+ value,
+ "Resolved: " + clipboardText,
+ "Text returned from `navigator.clipboard.read()` is as expected."
+ );
+ });
+ });
+});
+
+add_task(async function test_dismissing_paste_button() {
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false });
+ await promiseClickContentToTriggerClipboardRead(browser, false);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ const mutatedReadResultFromContentElement =
+ promiseMutatedReadResultFromContentElement(browser);
+ await promiseDismissPasteButton();
+ await pasteButtonIsHidden;
+ await mutatedReadResultFromContentElement.then(value => {
+ is(
+ value,
+ "Rejected: Clipboard read operation is not allowed.",
+ "`navigator.clipboard.read()` rejected after dismissing the 'Paste' button"
+ );
+ });
+ });
+});
+
+add_task(
+ async function test_multiple_read_invocations_for_same_user_activation() {
+ // Randomized text to avoid overlappings with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(
+ kContentFileUrl,
+ async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false });
+ await promiseClickContentToTriggerClipboardRead(browser, true);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+ const mutatedReadResultFromContentElement =
+ promiseMutatedReadResultFromContentElement(browser);
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await mutatedReadResultFromContentElement.then(value => {
+ is(
+ value,
+ "Resolved 1: " + clipboardText + "; Resolved 2: " + clipboardText,
+ "Two calls of `navigator.clipboard.read()` both resolved with the expected text."
+ );
+ });
+
+ // To avoid disturbing subsequent tests.
+ await pasteButtonIsHidden;
+ }
+ );
+ }
+);
+
+add_task(async function test_new_user_activation_shows_paste_button_again() {
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ // Ensure there's text on the clipboard.
+ await promiseWritingRandomTextToClipboard();
+
+ for (let i = 0; i < 2; ++i) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({ mustHaveAccessibleRule: false });
+ // A click initiates a new user activation.
+ await promiseClickContentToTriggerClipboardRead(browser, false);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ }
+ });
+});
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_readText.js b/dom/events/test/clipboard/browser_navigator_clipboard_readText.js
new file mode 100644
index 0000000000..f91976a822
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_readText.js
@@ -0,0 +1,241 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+
+const kContentFileName = "simple_navigator_clipboard_readText.html";
+
+const kContentFileUrl = kBaseUrlForContent + kContentFileName;
+
+const kApzTestNativeEventUtilsUrl =
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js";
+
+Services.scriptloader.loadSubScript(kApzTestNativeEventUtilsUrl, this);
+
+// @param aBrowser browser object of the content tab.
+// @param aMultipleReadTextCalls if false, exactly one call is made, two
+// otherwise.
+function promiseClickContentToTriggerClipboardReadText(
+ aBrowser,
+ aMultipleReadTextCalls
+) {
+ return promiseClickContentElement(
+ aBrowser,
+ aMultipleReadTextCalls ? "invokeReadTextTwiceId" : "invokeReadTextOnceId"
+ );
+}
+
+// @param aBrowser browser object of the content tab.
+function promiseMutatedReadTextResultFromContentElement(aBrowser) {
+ return promiseMutatedTextContentFromContentElement(
+ aBrowser,
+ "readTextResultId"
+ );
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.readText", true],
+ ["test.events.async.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test_paste_button_position() {
+ // Ensure there's text on the clipboard.
+ await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({
+ mustHaveAccessibleRule: false,
+ });
+ const coordsOfClickInContentRelativeToScreenInDevicePixels =
+ await promiseClickContentToTriggerClipboardReadText(browser, false);
+ info(
+ "coordsOfClickInContentRelativeToScreenInDevicePixels: " +
+ coordsOfClickInContentRelativeToScreenInDevicePixels.x +
+ ", " +
+ coordsOfClickInContentRelativeToScreenInDevicePixels.y
+ );
+ AccessibilityUtils.resetEnv();
+
+ const pasteButtonCoordsRelativeToScreenInDevicePixels =
+ await pasteButtonIsShown;
+ info(
+ "pasteButtonCoordsRelativeToScreenInDevicePixels: " +
+ pasteButtonCoordsRelativeToScreenInDevicePixels.x +
+ ", " +
+ pasteButtonCoordsRelativeToScreenInDevicePixels.y
+ );
+
+ const mouseCoordsRelativeToScreenInDevicePixels =
+ getMouseCoordsRelativeToScreenInDevicePixels();
+ info(
+ "mouseCoordsRelativeToScreenInDevicePixels: " +
+ mouseCoordsRelativeToScreenInDevicePixels.x +
+ ", " +
+ mouseCoordsRelativeToScreenInDevicePixels.y
+ );
+
+ // Asserting not overlapping is important; otherwise, when the
+ // "Paste" button is shown via a `mousedown` event, the following
+ // `mouseup` event could accept the "Paste" button unnoticed by the
+ // user.
+ ok(
+ isCloselyLeftOnTopOf(
+ mouseCoordsRelativeToScreenInDevicePixels,
+ pasteButtonCoordsRelativeToScreenInDevicePixels
+ ),
+ "'Paste' button is closely left on top of the mouse pointer."
+ );
+ ok(
+ isCloselyLeftOnTopOf(
+ coordsOfClickInContentRelativeToScreenInDevicePixels,
+ pasteButtonCoordsRelativeToScreenInDevicePixels
+ ),
+ "Coords of click in content are closely left on top of the 'Paste' button."
+ );
+
+ // To avoid disturbing subsequent tests.
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ });
+});
+
+add_task(async function test_accepting_paste_button() {
+ // Randomized text to avoid overlappings with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({
+ mustHaveAccessibleRule: false,
+ });
+ await promiseClickContentToTriggerClipboardReadText(browser, false);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ const mutatedReadTextResultFromContentElement =
+ promiseMutatedReadTextResultFromContentElement(browser);
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ await mutatedReadTextResultFromContentElement.then(value => {
+ is(
+ value,
+ "Resolved: " + clipboardText,
+ "Text returned from `navigator.clipboard.readText()` is as expected."
+ );
+ });
+ });
+});
+
+add_task(async function test_dismissing_paste_button() {
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({
+ mustHaveAccessibleRule: false,
+ });
+ await promiseClickContentToTriggerClipboardReadText(browser, false);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ const mutatedReadTextResultFromContentElement =
+ promiseMutatedReadTextResultFromContentElement(browser);
+ await promiseDismissPasteButton();
+ await pasteButtonIsHidden;
+ await mutatedReadTextResultFromContentElement.then(value => {
+ is(
+ value,
+ "Rejected.",
+ "`navigator.clipboard.readText()` rejected after dismissing the 'Paste' button"
+ );
+ });
+ });
+});
+
+add_task(
+ async function test_multiple_readText_invocations_for_same_user_activation() {
+ // Randomized text to avoid overlappings with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(
+ kContentFileUrl,
+ async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({
+ mustHaveAccessibleRule: false,
+ });
+ await promiseClickContentToTriggerClipboardReadText(browser, true);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+ const mutatedReadTextResultFromContentElement =
+ promiseMutatedReadTextResultFromContentElement(browser);
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await mutatedReadTextResultFromContentElement.then(value => {
+ is(
+ value,
+ "Resolved 1: " + clipboardText + "; Resolved 2: " + clipboardText,
+ "Two calls of `navigator.clipboard.read()` both resolved with the expected text."
+ );
+ });
+
+ // To avoid disturbing subsequent tests.
+ await pasteButtonIsHidden;
+ }
+ );
+ }
+);
+
+add_task(async function test_new_user_activation_shows_paste_button_again() {
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ // Ensure there's text on the clipboard.
+ await promiseWritingRandomTextToClipboard();
+
+ for (let i = 0; i < 2; ++i) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ // We intentionally turn off this a11y check, because the following click
+ // is send on an arbitrary web content that is not expected to be tested
+ // by itself with the browser mochitests, therefore this rule check shall
+ // be ignored by a11y-checks suite.
+ AccessibilityUtils.setEnv({
+ mustHaveAccessibleRule: false,
+ });
+ // A click initiates a new user activation.
+ await promiseClickContentToTriggerClipboardReadText(browser, false);
+ AccessibilityUtils.resetEnv();
+ await pasteButtonIsShown;
+
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ }
+ });
+});
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_readText_multiple.js b/dom/events/test/clipboard/browser_navigator_clipboard_readText_multiple.js
new file mode 100644
index 0000000000..1d0d2884b7
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_readText_multiple.js
@@ -0,0 +1,316 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const kContentFileName = "file_toplevel.html";
+const kContentFileUrl = kBaseUrlForContent + kContentFileName;
+
+async function waitForPasteContextMenu() {
+ await waitForPasteMenuPopupEvent("shown");
+ const pasteButton = document.getElementById(kPasteMenuItemId);
+ info("Wait for paste button enabled");
+ await BrowserTestUtils.waitForMutationCondition(
+ pasteButton,
+ { attributeFilter: ["disabled"] },
+ () => !pasteButton.disabled,
+ "Wait for paste button enabled"
+ );
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.readText", true],
+ ["test.events.async.enabled", true],
+ // Avoid paste button delay enabling making test too long.
+ ["security.dialog_enable_delay", 0],
+ ],
+ });
+});
+
+add_task(async function test_multiple_readText_from_same_frame_allow() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = waitForPasteContextMenu();
+ const readTextRequest1 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ await pasteButtonIsShown;
+
+ info("readText() from same frame again before interact with paste button");
+ const readTextRequest2 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ // Give some time for the second request to arrive parent process.
+ await SpecialPowers.spawn(browser, [], async () => {
+ return new Promise(resolve => {
+ content.setTimeout(resolve, 0);
+ });
+ });
+
+ info("Click paste button, both request should be resolved");
+ await promiseClickPasteButton();
+ is(
+ await readTextRequest1,
+ clipboardText,
+ "First request should be resolved"
+ );
+ is(
+ await readTextRequest2,
+ clipboardText,
+ "Second request should be resolved"
+ );
+ });
+});
+
+add_task(async function test_multiple_readText_from_same_frame_deny() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = waitForPasteContextMenu();
+ const readTextRequest1 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ await pasteButtonIsShown;
+
+ info("readText() from same frame again before interact with paste button");
+ const readTextRequest2 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ // Give some time for the second request to arrive parent process.
+ await SpecialPowers.spawn(browser, [], async () => {
+ return new Promise(resolve => {
+ content.setTimeout(resolve, 0);
+ });
+ });
+
+ info("Dismiss paste button, both request should be rejected");
+ await promiseDismissPasteButton();
+ await Assert.rejects(
+ readTextRequest1,
+ /NotAllowedError/,
+ "First request should be rejected"
+ );
+ await Assert.rejects(
+ readTextRequest2,
+ /NotAllowedError/,
+ "Second request should be rejected"
+ );
+ });
+});
+
+add_task(async function test_multiple_readText_from_same_origin_frame() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = waitForPasteContextMenu();
+ const readTextRequest1 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ await pasteButtonIsShown;
+
+ info(
+ "readText() from same origin child frame again before interacting with paste button"
+ );
+ const sameOriginFrame = browser.browsingContext.children[0];
+ const readTextRequest2 = SpecialPowers.spawn(
+ sameOriginFrame,
+ [],
+ async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ }
+ );
+ // Give some time for the second request to arrive parent process.
+ await SpecialPowers.spawn(sameOriginFrame, [], async () => {
+ return new Promise(resolve => {
+ content.setTimeout(resolve, 0);
+ });
+ });
+
+ info("Click paste button, both request should be resolved");
+ await promiseClickPasteButton();
+ is(
+ await readTextRequest1,
+ clipboardText,
+ "First request should be resolved"
+ );
+ is(
+ await readTextRequest2,
+ clipboardText,
+ "Second request should be resolved"
+ );
+ });
+});
+
+add_task(async function test_multiple_readText_from_cross_origin_frame() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = waitForPasteContextMenu();
+ const readTextRequest1 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ await pasteButtonIsShown;
+
+ info(
+ "readText() from different origin child frame again before interacting with paste button"
+ );
+ const crossOriginFrame = browser.browsingContext.children[1];
+ await Assert.rejects(
+ SpecialPowers.spawn(crossOriginFrame, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ }),
+ /NotAllowedError/,
+ "Second request should be rejected"
+ );
+
+ info("Click paste button, both request should be resolved");
+ await promiseClickPasteButton();
+ is(
+ await readTextRequest1,
+ clipboardText,
+ "First request should be resolved"
+ );
+ });
+});
+
+add_task(async function test_multiple_readText_from_background_frame() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ const backgroundTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ kContentFileUrl
+ );
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = waitForPasteContextMenu();
+ const readTextRequest1 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ await pasteButtonIsShown;
+
+ info(
+ "readText() from background tab again before interact with paste button"
+ );
+ await Assert.rejects(
+ SpecialPowers.spawn(backgroundTab.linkedBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ }),
+ /NotAllowedError/,
+ "Second request should be rejected"
+ );
+
+ info("Click paste button, both request should be resolved");
+ await promiseClickPasteButton();
+ is(
+ await readTextRequest1,
+ clipboardText,
+ "First request should be resolved"
+ );
+ });
+
+ await BrowserTestUtils.removeTab(backgroundTab);
+});
+
+add_task(async function test_multiple_readText_from_background_window() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const newWin = await BrowserTestUtils.openNewBrowserWindow();
+ const backgroundTab = await BrowserTestUtils.openNewForegroundTab(
+ newWin.gBrowser,
+ kContentFileUrl
+ );
+ await SimpleTest.promiseFocus(browser);
+
+ info("readText() from background window");
+ await Assert.rejects(
+ SpecialPowers.spawn(backgroundTab.linkedBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ }),
+ /NotAllowedError/,
+ "Request from background window should be rejected"
+ );
+
+ const pasteButtonIsShown = waitForPasteContextMenu();
+ const readTextRequest1 = SpecialPowers.spawn(browser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ });
+ await pasteButtonIsShown;
+
+ info(
+ "readText() from background window again before interact with paste button"
+ );
+ await Assert.rejects(
+ SpecialPowers.spawn(backgroundTab.linkedBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ }),
+ /NotAllowedError/,
+ "Second request should be rejected"
+ );
+
+ info("Click paste button, both request should be resolved");
+ await promiseClickPasteButton();
+ is(
+ await readTextRequest1,
+ clipboardText,
+ "First request should be resolved"
+ );
+
+ await BrowserTestUtils.closeWindow(newWin);
+ });
+});
+
+add_task(async function test_multiple_readText_focuse_in_chrome_document() {
+ // Randomized text to avoid overlapping with other tests.
+ const clipboardText = await promiseWritingRandomTextToClipboard();
+
+ const win = await BrowserTestUtils.openNewBrowserWindow();
+ const tab = await BrowserTestUtils.openNewForegroundTab(
+ win.gBrowser,
+ kContentFileUrl
+ );
+
+ info("Move focus to url bar");
+ win.gURLBar.focus();
+
+ info("readText() from web content");
+ await Assert.rejects(
+ SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ content.document.notifyUserGestureActivation();
+ return content.eval(`navigator.clipboard.readText();`);
+ }),
+ /NotAllowedError/,
+ "Request should be rejected when focus is not in content"
+ );
+
+ await BrowserTestUtils.closeWindow(win);
+});
diff --git a/dom/events/test/clipboard/browser_navigator_clipboard_touch.js b/dom/events/test/clipboard/browser_navigator_clipboard_touch.js
new file mode 100644
index 0000000000..aeea9a612d
--- /dev/null
+++ b/dom/events/test/clipboard/browser_navigator_clipboard_touch.js
@@ -0,0 +1,114 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const kBaseUrlForContent = getRootDirectory(gTestPath).replace(
+ "chrome://mochitests/content",
+ "https://example.com"
+);
+const kContentFileUrl =
+ kBaseUrlForContent + "simple_navigator_clipboard_readText.html";
+
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+ this
+);
+
+// @param aBrowser browser object of the content tab.
+// @param aContentElementId the ID of the element to be tapped.
+function promiseTouchTapContent(aBrowser, aContentElementId) {
+ return SpecialPowers.spawn(
+ aBrowser,
+ [aContentElementId],
+ async _contentElementId => {
+ await content.wrappedJSObject.waitUntilApzStable();
+
+ const contentElement = content.document.getElementById(_contentElementId);
+ let promise = new Promise(resolve => {
+ contentElement.addEventListener(
+ "click",
+ function (e) {
+ resolve({ x: e.screenX, y: e.screenY });
+ },
+ { once: true }
+ );
+ });
+
+ EventUtils.synthesizeTouchAtCenter(contentElement, {}, content.window);
+
+ return promise;
+ }
+ );
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.events.asyncClipboard.readText", true],
+ ["test.events.async.enabled", true],
+ ],
+ });
+});
+
+add_task(async function test_paste_button_position_touch() {
+ // Ensure there's text on the clipboard.
+ await promiseWritingRandomTextToClipboard();
+
+ await BrowserTestUtils.withNewTab(kContentFileUrl, async function (browser) {
+ const pasteButtonIsShown = promisePasteButtonIsShown();
+ const coordsOfClickInContentRelativeToScreenInDevicePixels =
+ await promiseTouchTapContent(browser, "invokeReadTextOnceId");
+ info(
+ "coordsOfClickInContentRelativeToScreenInDevicePixels: " +
+ coordsOfClickInContentRelativeToScreenInDevicePixels.x +
+ ", " +
+ coordsOfClickInContentRelativeToScreenInDevicePixels.y
+ );
+
+ const pasteButtonCoordsRelativeToScreenInDevicePixels =
+ await pasteButtonIsShown;
+ info(
+ "pasteButtonCoordsRelativeToScreenInDevicePixels: " +
+ pasteButtonCoordsRelativeToScreenInDevicePixels.x +
+ ", " +
+ pasteButtonCoordsRelativeToScreenInDevicePixels.y
+ );
+
+ const mouseCoordsRelativeToScreenInDevicePixels =
+ getMouseCoordsRelativeToScreenInDevicePixels();
+ info(
+ "mouseCoordsRelativeToScreenInDevicePixels: " +
+ mouseCoordsRelativeToScreenInDevicePixels.x +
+ ", " +
+ mouseCoordsRelativeToScreenInDevicePixels.y
+ );
+
+ // Asserting not overlapping is important; otherwise, when the
+ // "Paste" button is shown via a `mousedown` event, the following
+ // `mouseup` event could accept the "Paste" button unnoticed by the
+ // user.
+ ok(
+ isCloselyLeftOnTopOf(
+ mouseCoordsRelativeToScreenInDevicePixels,
+ pasteButtonCoordsRelativeToScreenInDevicePixels
+ ),
+ "'Paste' button is closely left on top of the mouse pointer."
+ );
+ ok(
+ isCloselyLeftOnTopOf(
+ coordsOfClickInContentRelativeToScreenInDevicePixels,
+ pasteButtonCoordsRelativeToScreenInDevicePixels
+ ),
+ "Coords of click in content are closely left on top of the 'Paste' button."
+ );
+
+ // To avoid disturbing subsequent tests.
+ const pasteButtonIsHidden = promisePasteButtonIsHidden();
+ await promiseClickPasteButton();
+ await pasteButtonIsHidden;
+ });
+});
diff --git a/dom/events/test/clipboard/chrome.toml b/dom/events/test/clipboard/chrome.toml
new file mode 100644
index 0000000000..0a3107e389
--- /dev/null
+++ b/dom/events/test/clipboard/chrome.toml
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+["test_async_clipboard.xhtml"]
diff --git a/dom/events/test/clipboard/file_iframe.html b/dom/events/test/clipboard/file_iframe.html
new file mode 100644
index 0000000000..a581da6ef0
--- /dev/null
+++ b/dom/events/test/clipboard/file_iframe.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<body>Dummy page</body>
diff --git a/dom/events/test/clipboard/file_toplevel.html b/dom/events/test/clipboard/file_toplevel.html
new file mode 100644
index 0000000000..60661ca5e2
--- /dev/null
+++ b/dom/events/test/clipboard/file_toplevel.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ </head>
+ <body>
+ <iframe src="file_iframe.html"></iframe>
+ <iframe src="https://example.org/browser/dom/events/test/clipboard/file_iframe.html"></iframe>
+ </body>
+</html>
diff --git a/dom/events/test/clipboard/head.js b/dom/events/test/clipboard/head.js
new file mode 100644
index 0000000000..73404ca535
--- /dev/null
+++ b/dom/events/test/clipboard/head.js
@@ -0,0 +1,185 @@
+/* -*- Mode: JavaScript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const kPasteMenuPopupId = "clipboardReadPasteMenuPopup";
+const kPasteMenuItemId = "clipboardReadPasteMenuItem";
+
+function promiseWritingRandomTextToClipboard() {
+ const clipboardText = "X" + Math.random();
+ return navigator.clipboard.writeText(clipboardText).then(() => {
+ return clipboardText;
+ });
+}
+
+function promiseBrowserReflow() {
+ return new Promise(resolve =>
+ requestAnimationFrame(() => requestAnimationFrame(resolve))
+ );
+}
+
+function waitForPasteMenuPopupEvent(aEventSuffix) {
+ // The element with id `kPasteMenuPopupId` is inserted dynamically, hence
+ // calling `BrowserTestUtils.waitForEvent` instead of
+ // `BrowserTestUtils.waitForPopupEvent`.
+ return BrowserTestUtils.waitForEvent(
+ document,
+ "popup" + aEventSuffix,
+ false /* capture */,
+ e => {
+ return e.target.getAttribute("id") == kPasteMenuPopupId;
+ }
+ );
+}
+
+function promisePasteButtonIsShown() {
+ return waitForPasteMenuPopupEvent("shown").then(async () => {
+ ok(true, "Witnessed 'popupshown' event for 'Paste' button.");
+
+ const pasteButton = document.getElementById(kPasteMenuItemId);
+ ok(
+ pasteButton.disabled,
+ "Paste button should be shown with disabled by default"
+ );
+ await BrowserTestUtils.waitForMutationCondition(
+ pasteButton,
+ { attributeFilter: ["disabled"] },
+ () => !pasteButton.disabled,
+ "Wait for paste button enabled"
+ );
+
+ return promiseBrowserReflow().then(() => {
+ return coordinatesRelativeToScreen({
+ target: pasteButton,
+ offsetX: 0,
+ offsetY: 0,
+ });
+ });
+ });
+}
+
+function promisePasteButtonIsHidden() {
+ return waitForPasteMenuPopupEvent("hidden").then(() => {
+ ok(true, "Witnessed 'popuphidden' event for 'Paste' button.");
+ return promiseBrowserReflow();
+ });
+}
+
+function promiseClickPasteButton() {
+ const pasteButton = document.getElementById(kPasteMenuItemId);
+ let promise = BrowserTestUtils.waitForEvent(pasteButton, "click");
+ EventUtils.synthesizeMouseAtCenter(pasteButton, {});
+ return promise;
+}
+
+function getMouseCoordsRelativeToScreenInDevicePixels() {
+ let mouseXInCSSPixels = {};
+ let mouseYInCSSPixels = {};
+ window.windowUtils.getLastOverWindowPointerLocationInCSSPixels(
+ mouseXInCSSPixels,
+ mouseYInCSSPixels
+ );
+
+ return {
+ x:
+ (mouseXInCSSPixels.value + window.mozInnerScreenX) *
+ window.devicePixelRatio,
+ y:
+ (mouseYInCSSPixels.value + window.mozInnerScreenY) *
+ window.devicePixelRatio,
+ };
+}
+
+function isCloselyLeftOnTopOf(aCoordsP1, aCoordsP2, aDelta = 10) {
+ return (
+ Math.abs(aCoordsP2.x - aCoordsP1.x) < aDelta &&
+ Math.abs(aCoordsP2.y - aCoordsP1.y) < aDelta
+ );
+}
+
+async function promiseDismissPasteButton() {
+ // We intentionally turn off this a11y check, because the following click
+ // is send on the <body> to dismiss the pending popup using an alternative way
+ // of the popup dismissal, where the other way like `Esc` key is available,
+ // therefore this test can be ignored.
+ AccessibilityUtils.setEnv({
+ mustHaveAccessibleRule: false,
+ });
+ // nsXULPopupManager rollup is handled in widget code, so we have to
+ // synthesize native mouse events.
+ await EventUtils.promiseNativeMouseEvent({
+ type: "click",
+ target: document.body,
+ // Relies on the assumption that the center of chrome document doesn't
+ // overlay with the paste button showed for clipboard readText request.
+ atCenter: true,
+ });
+ // Move mouse away to avoid subsequence tests showing paste button in
+ // thie dismissing location.
+ await EventUtils.promiseNativeMouseEvent({
+ type: "mousemove",
+ target: document.body,
+ offsetX: 100,
+ offsetY: 100,
+ });
+ AccessibilityUtils.resetEnv();
+}
+
+// @param aBrowser browser object of the content tab.
+// @param aContentElementId the ID of the element to be clicked.
+function promiseClickContentElement(aBrowser, aContentElementId) {
+ return SpecialPowers.spawn(
+ aBrowser,
+ [aContentElementId],
+ async _contentElementId => {
+ const contentElement = content.document.getElementById(_contentElementId);
+ let promise = new Promise(resolve => {
+ contentElement.addEventListener(
+ "click",
+ function (e) {
+ resolve({ x: e.screenX, y: e.screenY });
+ },
+ { once: true }
+ );
+ });
+
+ EventUtils.synthesizeMouseAtCenter(contentElement, {}, content.window);
+
+ return promise;
+ }
+ );
+}
+
+// @param aBrowser browser object of the content tab.
+// @param aContentElementId the ID of the element to observe.
+function promiseMutatedTextContentFromContentElement(
+ aBrowser,
+ aContentElementId
+) {
+ return SpecialPowers.spawn(
+ aBrowser,
+ [aContentElementId],
+ async _contentElementId => {
+ const contentElement = content.document.getElementById(_contentElementId);
+
+ const promiseTextContentResult = new Promise(resolve => {
+ const mutationObserver = new content.MutationObserver(
+ (aMutationRecord, aMutationObserver) => {
+ info("Observed mutation.");
+ aMutationObserver.disconnect();
+ resolve(contentElement.textContent);
+ }
+ );
+
+ mutationObserver.observe(contentElement, {
+ childList: true,
+ });
+ });
+
+ return await promiseTextContentResult;
+ }
+ );
+}
diff --git a/dom/events/test/clipboard/mochitest.toml b/dom/events/test/clipboard/mochitest.toml
new file mode 100644
index 0000000000..7829915267
--- /dev/null
+++ b/dom/events/test/clipboard/mochitest.toml
@@ -0,0 +1,7 @@
+[DEFAULT]
+
+["test_paste_image.html"]
+skip-if = [
+ "headless", # Bug 1405869
+ "os == 'android'", # Image type isn't supported
+]
diff --git a/dom/events/test/clipboard/simple_navigator_clipboard_keydown.html b/dom/events/test/clipboard/simple_navigator_clipboard_keydown.html
new file mode 100644
index 0000000000..15fcdfba2c
--- /dev/null
+++ b/dom/events/test/clipboard/simple_navigator_clipboard_keydown.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <script>
+ function onLoad() {
+ document.addEventListener("keydown", function() {
+ navigator.clipboard.readText();
+ });
+ }
+ </script>
+ </head>
+ <body onload="onLoad()">
+ </body>
+</html>
diff --git a/dom/events/test/clipboard/simple_navigator_clipboard_read.html b/dom/events/test/clipboard/simple_navigator_clipboard_read.html
new file mode 100644
index 0000000000..825089e799
--- /dev/null
+++ b/dom/events/test/clipboard/simple_navigator_clipboard_read.html
@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <!-- Required by the .js part of the test. In a more ideal world, the script
+ could be loaded in the .js part; however, currently, that causes other
+ problems, which would require other changes in test framework code. -->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+ <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+
+ <script>
+ function onLoad() {
+ const readResult = document.getElementById("readResultId");
+
+ async function getClipboardText() {
+ let items = await navigator.clipboard.read();
+ if (items.length != 1) {
+ throw Error(`incorrect number of clipboard item (${items.length})`);
+ }
+
+ let item = items[0];
+ for (let type of item.types) {
+ if (type == "text/plain") {
+ let blob = await item.getType(type);
+ return await blob.text();
+ }
+ }
+
+ throw Error("no text/plain type");
+ }
+
+ const b1 = document.getElementById("invokeReadOnceId");
+ b1.addEventListener("click", async () => {
+ getClipboardText().then(text => {
+ readResult.textContent = `Resolved: ${text}`;
+ }, (e) => { readResult.textContent = `Rejected: ${e.message}`});
+ });
+
+ const b2 = document.getElementById("invokeReadTwiceId");
+ b2.addEventListener("click", async () => {
+ const t1 = getClipboardText();
+ const t2 = getClipboardText();
+
+ const r1 = await t1.then(text => {
+ return `Resolved 1: ${text}`;
+ }, (e) => { return `Rejected 1: ${e.message}`;});
+
+ const r2 = await t2.then(text => {
+ return "Resolved 2: " + text;
+ }, (e) => { return `Rejected 2: ${e.message}`;});
+
+ readResult.textContent = r1 + "; " + r2;
+ });
+ }
+ </script>
+ </head>
+ <body onload="onLoad()">
+ <button id="invokeReadOnceId">1</button>
+ <button id="invokeReadTwiceId">2</button>
+ <div id="readResultId"/>
+ </body>
+</html>
diff --git a/dom/events/test/clipboard/simple_navigator_clipboard_readText.html b/dom/events/test/clipboard/simple_navigator_clipboard_readText.html
new file mode 100644
index 0000000000..0b85371091
--- /dev/null
+++ b/dom/events/test/clipboard/simple_navigator_clipboard_readText.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <!-- Required by the .js part of the test. In a more ideal world, the script
+ could be loaded in the .js part; however, currently, that causes other
+ problems, which would require other changes in test framework code. -->
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+ <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+
+ <script>
+ function onLoad() {
+ const readTextResult = document.getElementById("readTextResultId");
+
+ const b1 = document.getElementById("invokeReadTextOnceId");
+ b1.addEventListener("click", async () => {
+ navigator.clipboard.readText().then(text => {
+ readTextResult.textContent = "Resolved: " + text;
+ }, () => { readTextResult.textContent = "Rejected." });
+ });
+
+ const b2 = document.getElementById("invokeReadTextTwiceId");
+ b2.addEventListener("click", async () => {
+ const t1 = navigator.clipboard.readText();
+ const t2 = navigator.clipboard.readText();
+
+ const r1 = await t1.then(text => {
+ return "Resolved 1: " + text;
+ }, () => { return "Rejected: 1";});
+
+ const r2 = await t2.then(text => {
+ return "Resolved 2: " + text;
+ }, () => { return "Rejected: 2";});
+
+ readTextResult.textContent = r1 + "; " + r2;
+ });
+ }
+ </script>
+ </head>
+ <body onload="onLoad()">
+ <button id="invokeReadTextOnceId">1</button>
+ <button id="invokeReadTextTwiceId">2</button>
+ <div id="readTextResultId"/>
+ </body>
+</html>
diff --git a/dom/events/test/clipboard/test_async_clipboard.xhtml b/dom/events/test/clipboard/test_async_clipboard.xhtml
new file mode 100644
index 0000000000..ec54809077
--- /dev/null
+++ b/dom/events/test/clipboard/test_async_clipboard.xhtml
@@ -0,0 +1,124 @@
+<?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="Async clipboard APIs Test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="runTest();">
+
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+ SimpleTest.waitForExplicitFinish();
+
+ const Services = SpecialPowers.Services;
+
+ const kTextPlainMimeType = "text/plain";
+
+ function clearClipboard() {
+ Services.clipboard.emptyClipboard(Services.clipboard.kGlobalClipboard);
+ }
+
+ async function testRead() {
+ let expected = "x";
+ await SimpleTest.promiseClipboardChange(expected, () => {
+ SpecialPowers.clipboardCopyString(expected);
+ }, kTextPlainMimeType);
+ let items = await navigator.clipboard.read();
+ is(items.length, 1, "read() read exactly one item");
+ const actual = await items[0].getType(kTextPlainMimeType).then(blob => blob.text());
+ is(actual, expected, "read() read the right thing");
+ }
+
+ async function testWrite() {
+ await SimpleTest.promiseClipboardChange("", () => {
+ clearClipboard();
+ });
+
+ let expected = "x";
+ // eslint-disable-next-line no-undef
+ let item = new ClipboardItem({[kTextPlainMimeType]: expected});
+ await navigator.clipboard.write([item]);
+ let actual = SpecialPowers.getClipboardData(kTextPlainMimeType);
+ is(actual, expected, "write() wrote the right thing");
+ }
+
+ async function testReadText() {
+ let expected = "x";
+ await SimpleTest.promiseClipboardChange(expected, () => {
+ SpecialPowers.clipboardCopyString(expected);
+ }, kTextPlainMimeType);
+ let actual = await navigator.clipboard.readText();
+ is(actual, expected, "readText() read the right thing");
+ }
+
+ async function testWriteText() {
+ await SimpleTest.promiseClipboardChange("", () => {
+ clearClipboard();
+ });
+
+ let expected = "x";
+ await navigator.clipboard.writeText(expected);
+ let actual = SpecialPowers.getClipboardData(kTextPlainMimeType);
+ is(actual, expected, "writeText() wrote the right thing");
+ }
+
+ async function testNoContentsRead() {
+ await SimpleTest.promiseClipboardChange("", () => {
+ clearClipboard();
+ });
+
+ const items = await navigator.clipboard.read();
+
+ // Bug 1756955: at least on Ubuntu 20.04, clearing the clipboard leads to
+ // one item with no types.
+ if (!items.length ||
+ (items.length == 1 && !items[0].types.length)) {
+ ok(true, "read() read the right thing from empty clipboard");
+ } else {
+ ok(false, "read() read the wrong thing from empty clipboard");
+ }
+ }
+
+ async function testNoContentsReadText() {
+ await SimpleTest.promiseClipboardChange("", () => {
+ clearClipboard();
+ });
+ let actual = await navigator.clipboard.readText();
+ is(actual, "", "readText() read the right thing from empty clipboard");
+ }
+
+ function runTest() {
+ (async function() {
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["dom.events.asyncClipboard.clipboardItem", true],
+ ]});
+ await testRead();
+ await testReadText();
+ await testWrite();
+ await testWriteText();
+
+ await testNoContentsRead();
+ await testNoContentsReadText();
+
+ 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/dom/events/test/clipboard/test_paste_image.html b/dom/events/test/clipboard/test_paste_image.html
new file mode 100644
index 0000000000..061b577657
--- /dev/null
+++ b/dom/events/test/clipboard/test_paste_image.html
@@ -0,0 +1,213 @@
+<html><head>
+<title>Test for bug 891247</title>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+
+<script class="testbody" type="application/javascript">
+ function ImageTester() {
+ var counter = 0;
+ var images = [];
+ var that = this;
+
+ this.add = function(aFile) {
+ images.push(aFile);
+ };
+
+ this.test = async function() {
+ for (var i = 0; i < images.length; i++) {
+ await testImageSize(images[i]);
+ }
+ };
+
+ this.returned = function() {
+ counter++;
+ info("returned=" + counter + " images.length=" + images.length);
+ if (counter == images.length) {
+ info("test finish");
+ }
+ };
+
+ async function testImageSize(aFile) {
+ var source = window.URL.createObjectURL(aFile);
+ var image = new Image();
+ image.src = source;
+ var imageTester = that;
+ let promise = new Promise(resolve => {
+ image.addEventListener("load", function(e) {
+ is(this.width, 62, "Check generated image width");
+ is(this.height, 71, "Check generated image height");
+
+ // This fails on OSX only.
+ if (!navigator.platform.includes("Mac")) {
+ testImageCanvas(image);
+ }
+
+ imageTester.returned();
+ resolve();
+ }, { once: true });
+ });
+
+ document.body.appendChild(image);
+ await promise;
+ };
+
+ function testImageCanvas(aImage) {
+ var canvas = drawToCanvas(aImage);
+
+ var refImage = document.getElementById('image');
+ var refCanvas = drawToCanvas(refImage);
+
+ is(canvas.toDataURL(), refCanvas.toDataURL(), "Image should map pixel-by-pixel");
+ }
+
+ function drawToCanvas(aImage) {
+ var canvas = document.createElement("CANVAS");
+ document.body.appendChild(canvas);
+ canvas.width = aImage.width;
+ canvas.height = aImage.height;
+ canvas.getContext('2d').drawImage(aImage, 0, 0);
+ return canvas;
+ }
+ }
+
+ function copyImage(aImageId) {
+ // selection of the node
+ var node = document.getElementById(aImageId);
+ var docShell = SpecialPowers.wrap(window).docShell;
+
+ // let's copy the node
+ var documentViewer = docShell.docViewer
+ .QueryInterface(SpecialPowers.Ci.nsIDocumentViewerEdit);
+ documentViewer.setCommandNode(node);
+ documentViewer.copyImage(documentViewer.COPY_IMAGE_ALL);
+ }
+
+ async function doTest(imageAsFileEnabled) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["clipboard.imageAsFile.enabled", imageAsFileEnabled]],
+ });
+
+ copyImage('image');
+
+ //--------- now check the content of the clipboard
+ var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
+ .getService(SpecialPowers.Ci.nsIClipboard);
+ // does the clipboard contain text/plain data ?
+ ok(clipboard.hasDataMatchingFlavors(["text/plain"], clipboard.kGlobalClipboard),
+ "clipboard contains unicode text");
+ // does the clipboard contain text/html data ?
+ ok(clipboard.hasDataMatchingFlavors(["text/html"], clipboard.kGlobalClipboard),
+ "clipboard contains html text");
+ // does the clipboard contain image data ?
+ ok(clipboard.hasDataMatchingFlavors(["image/png"], clipboard.kGlobalClipboard),
+ "clipboard contains image");
+
+ let promise = new Promise(resolve => {
+ window.addEventListener("paste", async (e) => {
+ isDeeply(e.clipboardData.types,
+ (navigator.platform.includes("Win") && imageAsFileEnabled) ?
+ ["application/x-moz-file", "Files"] : ["text/html", "text/plain", "Files"]);
+ await onPaste(e, imageAsFileEnabled);
+ resolve();
+ }, { once: true });
+ });
+
+ var textarea = SpecialPowers.wrap(document.getElementById('textarea'));
+ textarea.focus();
+ textarea.editor.paste(clipboard.kGlobalClipboard);
+
+ await promise;
+
+ clipboard.emptyClipboard(clipboard.kGlobalClipboard);
+ }
+
+ async function onPaste(e, imageAsFileEnabled) {
+ var imageTester = new ImageTester;
+ testFiles(e, imageTester, imageAsFileEnabled);
+ testItems(e, imageTester);
+ await imageTester.test();
+ }
+
+ function testItems(e, imageTester) {
+ var items = e.clipboardData.items;
+ is(items, e.clipboardData.items,
+ "Getting @items twice should return the same object");
+ var haveFiles = false;
+ ok(items instanceof DataTransferItemList, "@items implements DataTransferItemList");
+ ok(items.length, "@items is not empty");
+ for (var i = 0; i < items.length; i++) {
+ var item = items[i];
+ ok(item instanceof DataTransferItem, "each element of @items must implement DataTransferItem");
+ if (item.kind == "file") {
+ var file = item.getAsFile();
+ ok(file instanceof File, ".getAsFile() returns a File object");
+ ok(file.size > 0, "Files shouldn't have size 0");
+ imageTester.add(file);
+ }
+ }
+ }
+
+ function testFiles(e, imageTester, imageAsFileEnabled) {
+ var files = e.clipboardData.files;
+
+ is(files, e.clipboardData.files,
+ "Getting the files array twice should return the same array");
+ is(files.length, 1, "There should be one file in the clipboard");
+ for (var i = 0; i < files.length; i++) {
+ var file = files[i];
+ ok(file instanceof File, ".files should contain only File objects");
+ ok(file.size > 0, "This file shouldn't have size 0");
+ if (navigator.platform.includes("Win") && imageAsFileEnabled) {
+ ok(file.name.startsWith("Untitled") && file.name.endsWith(".png"),
+ `Check filename, got "${file.name}"`);
+ } else {
+ is(file.name, "image.png", "Check filename");
+ }
+
+ testSlice(file);
+ imageTester.add(file);
+ // Adding the same image again so we can test concurrency
+ imageTester.add(file);
+ }
+ }
+
+ function testSlice(aFile) {
+ var blob = aFile.slice();
+ ok(blob instanceof Blob, ".slice returns a blob");
+ is(blob.size, aFile.size, "the blob has the same size");
+
+ blob = aFile.slice(123123);
+ is(blob.size, 0, ".slice overflow check");
+
+ blob = aFile.slice(123, 123141);
+ is(blob.size, aFile.size - 123, ".slice @size check");
+
+ blob = aFile.slice(123, 12);
+ is(blob.size, 0, ".slice @size check 2");
+
+ blob = aFile.slice(124, 134, "image/png");
+ is(blob.size, 10, ".slice @size check 3");
+ is(blob.type, "image/png", ".slice @type check");
+ }
+
+ add_task(async function test_imageAsFile_enabled() {
+ await doTest(true);
+ });
+
+ add_task(async function test_imageAsFile_disabled() {
+ await doTest(false);
+ });
+
+</script>
+<body>
+ <img id="image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABHCA
+ IAAADQjmMaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3goUAwAgSAORBwAAABl0RVh0Q29
+ tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAABPSURBVGje7c4BDQAACAOga//OmuMbJGAurTbq
+ 6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6s31B0IqAY2/t
+ QVCAAAAAElFTkSuQmCC" />
+ <form>
+ <textarea id="textarea"></textarea>
+ </form>
+</body>
+</html>
diff --git a/dom/events/test/dragimage.html b/dom/events/test/dragimage.html
new file mode 100644
index 0000000000..059b1de90f
--- /dev/null
+++ b/dom/events/test/dragimage.html
@@ -0,0 +1,10 @@
+<html>
+<head>
+<title>Drag Image Test</title>
+</head>
+<body>
+<img src="green.png" width=100 height=100>
+<div id='over' style='width: 100px; height: 100px; border: 1px solid black;'
+ ondragover="event.preventDefault();"></div>
+</body>
+</html>
diff --git a/dom/events/test/empty.js b/dom/events/test/empty.js
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/dom/events/test/empty.js
diff --git a/dom/events/test/error_event_worker.js b/dom/events/test/error_event_worker.js
new file mode 100644
index 0000000000..13422c6974
--- /dev/null
+++ b/dom/events/test/error_event_worker.js
@@ -0,0 +1,19 @@
+addEventListener("error", function (e) {
+ var obj = {};
+ for (var prop of ["message", "filename", "lineno"]) {
+ obj[prop] = e[prop];
+ }
+ obj.type = "event";
+ postMessage(obj);
+});
+onerror = function (message, filename, lineno) {
+ var obj = {
+ message,
+ filename,
+ lineno,
+ type: "callback",
+ };
+ postMessage(obj);
+ return false;
+};
+throw new Error("workerhello");
diff --git a/dom/events/test/event_leak_utils.js b/dom/events/test/event_leak_utils.js
new file mode 100644
index 0000000000..86b26a4136
--- /dev/null
+++ b/dom/events/test/event_leak_utils.js
@@ -0,0 +1,84 @@
+// Any copyright is dedicated to the Public Domain.
+// http://creativecommons.org/publicdomain/zero/1.0/
+"use strict";
+
+// This function runs a number of tests where:
+//
+// 1. An iframe is created
+// 2. The target callback is executed with the iframe's contentWindow as
+// an argument.
+// 3. The iframe is destroyed and GC is forced.
+// 4. Verifies that the iframe's contentWindow has been GC'd.
+//
+// Different ways of destroying the iframe are checked. Simple
+// remove(), destruction via bfcache, or replacement by document.open().
+//
+// Please pass a target callback that exercises the API under
+// test using the given window. The callback should try to leave the
+// API active to increase the liklihood of provoking an API. Any activity
+// should be canceled by the destruction of the window.
+async function checkForEventListenerLeaks(name, target) {
+ // Test if we leak in the case where we do nothing special to
+ // the frame before removing it from the DOM.
+ await _eventListenerLeakStep(target, `${name} default`);
+
+ // Test the case where we navigate the frame before removing it
+ // from the DOM so that the window using the target API ends up
+ // in bfcache.
+ await _eventListenerLeakStep(target, `${name} bfcache`, frame => {
+ frame.src = "about:blank";
+ return new Promise(resolve => (frame.onload = resolve));
+ });
+
+ // Test the case where we document.open() the frame before removing
+ // it from the DOM so that the window using the target API ends
+ // up getting replaced.
+ await _eventListenerLeakStep(target, `${name} document.open()`, frame => {
+ frame.contentDocument.open();
+ frame.contentDocument.close();
+ });
+}
+
+// ----------------
+// Internal helpers
+// ----------------
+
+// Utility function to create a loaded iframe.
+async function _withFrame(doc, url) {
+ let frame = doc.createElement("iframe");
+ frame.src = url;
+ doc.body.appendChild(frame);
+ await new Promise(resolve => (frame.onload = resolve));
+ return frame;
+}
+
+// This function defines the basic form of the test cases. We create an
+// iframe, execute the target callback to manipulate the DOM, remove the frame
+// from the DOM, and then check to see if the frame was GC'd. The caller
+// may optionally pass in a callback that will be executed with the
+// frame as an argument before removing it from the DOM.
+async function _eventListenerLeakStep(target, name, extra) {
+ let frame = await _withFrame(document, "empty.html");
+
+ await target(frame.contentWindow);
+
+ let weakRef = SpecialPowers.Cu.getWeakReference(frame.contentWindow);
+ ok(weakRef.get(), `should be able to create a weak reference - ${name}`);
+
+ if (extra) {
+ await extra(frame);
+ }
+
+ frame.remove();
+ frame = null;
+
+ // Perform many GC's to avoid intermittent delayed collection.
+ await new Promise(resolve => SpecialPowers.exactGC(resolve));
+ await new Promise(resolve => SpecialPowers.exactGC(resolve));
+ await new Promise(resolve => SpecialPowers.exactGC(resolve));
+
+ ok(
+ !weakRef.get(),
+ `iframe content window should be garbage collected - ${name}`
+ );
+}
diff --git a/dom/events/test/file_beforeinput_by_execCommand_in_contentscript.html b/dom/events/test/file_beforeinput_by_execCommand_in_contentscript.html
new file mode 100644
index 0000000000..7610f201ae
--- /dev/null
+++ b/dom/events/test/file_beforeinput_by_execCommand_in_contentscript.html
@@ -0,0 +1,9 @@
+<!doctype html>
+<div contenteditable>abcdef</div>
+<div contenteditable>abcdef</div>
+<script>
+document.querySelectorAll("[contenteditable]")[1].addEventListener("beforeinput", event => {
+ document.execCommand("insertText", false, "ABCDEF");
+ event.preventDefault();
+});
+</script>
diff --git a/dom/events/test/file_bug1446834.html b/dom/events/test/file_bug1446834.html
new file mode 100644
index 0000000000..e3832fd2f0
--- /dev/null
+++ b/dom/events/test/file_bug1446834.html
@@ -0,0 +1,96 @@
+<html>
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+
+ function lazyRequestAnimationFrame(fn) {
+ requestAnimationFrame(
+ function() {
+ setTimeout(fn);
+ });
+ }
+
+ var tests = [ removeHost, removeShadowElement ];
+ function nextTest() {
+ if (tests.length) {
+ var test = tests.shift();
+ lazyRequestAnimationFrame(test);
+ } else {
+ parent.SimpleTest.finish();
+ }
+ }
+
+ function removeHost() {
+ var hostgrandparent = document.getElementById("hostgrandparent");
+ var hostparent = document.getElementById("hostparent");
+ hostparent.innerHTML = "<div id='host'></div>";
+ var host = document.getElementById("host");
+ var sr = document.getElementById("host").attachShadow({mode: "open"});
+ sr.innerHTML = "<input type='button' value='click'>";
+ sr.firstChild.onclick = function() {
+ parent.is(hostparent.querySelector("div:hover"), host, "host should be hovered.");
+ host.remove();
+ parent.is(hostgrandparent.querySelector("div:hover"), hostparent,
+ "hostgrandparent element should have descendants marked in :hover state.");
+ synthesizeMouseAtCenter(document.getElementById('light'), { type: "mousemove" });
+ lazyRequestAnimationFrame(
+ function() {
+ parent.is(hostgrandparent.querySelector("div:hover"), null,
+ "hostgrandparent element shouldn't have descendants marked in :hover state anymore.");
+ nextTest();
+ }
+ );
+ }
+ lazyRequestAnimationFrame(
+ function() {
+ synthesizeMouseAtCenter(sr.firstChild, { type: "mousemove" });
+ synthesizeMouseAtCenter(sr.firstChild, {});
+ }
+ );
+ }
+
+ function removeShadowElement() {
+ var hostgrandparent = document.getElementById("hostgrandparent");
+ var hostparent = document.getElementById("hostparent");
+ hostparent.innerHTML = "<div id='host'><input id='input' slot='slot' type='button' value='click'></div>";
+ var host = document.getElementById("host");
+ var input = document.getElementById("input");
+ var sr = document.getElementById("host").attachShadow({mode: "open"});
+ sr.innerHTML = "<div><div><slot name='slot'></slot></div></div>";
+ var shadowOuterDiv = sr.firstChild;
+ var shadowInnerDiv = shadowOuterDiv.firstChild;
+ var slot = shadowInnerDiv.firstChild;
+ sr.firstChild.onclick = function() {
+ parent.is(hostparent.querySelector("div:hover"), host, "host should be hovered.");
+ slot.remove();
+ parent.is(shadowOuterDiv.querySelector("div:hover"), shadowInnerDiv,
+ "Elements in shadow DOM should stay hovered");
+ synthesizeMouseAtCenter(document.getElementById('light'), { type: "mousemove" });
+ lazyRequestAnimationFrame(
+ function() {
+ parent.is(shadowOuterDiv.querySelector("div:hover"), null,
+ "Shadow DOM shouldn't be marked to be hovered anymore.");
+ nextTest();
+ }
+ );
+ }
+ lazyRequestAnimationFrame(
+ function() {
+ synthesizeMouseAtCenter(input, { type: "mousemove" });
+ synthesizeMouseAtCenter(input, {});
+ }
+ );
+ }
+ </script>
+ <style>
+ </style>
+ </head>
+ <body onload="nextTest()">
+ <div id="hostgrandparent">
+ <div id="hostparent">
+ </div>
+ foo
+ </div>
+ <div id="light">light dom</div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/file_bug1484371.html b/dom/events/test/file_bug1484371.html
new file mode 100644
index 0000000000..56c284b733
--- /dev/null
+++ b/dom/events/test/file_bug1484371.html
@@ -0,0 +1,94 @@
+<html>
+ <head>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ var mouseEnterCount = 0;
+ function mouseEnter() {
+ ++mouseEnterCount;
+ }
+ var mouseLeaveCount = 0;
+ function mouseLeave() {
+ ++mouseLeaveCount;
+ }
+
+ var pointerEnterCount = 0;
+ function pointerEnter() {
+ ++pointerEnterCount;
+ }
+ var pointerLeaveCount = 0;
+ function pointerLeave() {
+ ++pointerLeaveCount;
+ }
+
+ function pointerEventsEnabled() {
+ return "onpointerenter" in document.body;
+ }
+
+ function checkEventCounts(expected, msg) {
+ parent.is(mouseEnterCount, expected.mouseEnterCount, msg + ": mouseenter event count");
+ parent.is(mouseLeaveCount, expected.mouseLeaveCount, msg + ": mouseleave event count");
+ if (pointerEventsEnabled()) {
+ parent.is(pointerEnterCount, expected.pointerEnterCount, msg + ": pointerenter event count");
+ parent.is(pointerLeaveCount, expected.pointerLeaveCount, msg + ": pointerleave event count");
+ }
+ }
+
+ function test() {
+ var lightDiv = document.getElementById("lightDiv");
+ var host = document.getElementById("host");
+ var sr = host.attachShadow({mode: "closed"});
+ sr.innerHTML = "<div>shadow DOM<div>";
+ var shadowDiv = sr.firstChild;
+
+ host.addEventListener("mouseenter", mouseEnter, true);
+ host.addEventListener("mouseleave", mouseLeave, true);
+ host.addEventListener("pointerenter", pointerEnter, true);
+ host.addEventListener("pointerleave", pointerLeave, true);
+
+ shadowDiv.addEventListener("mouseenter", mouseEnter, true);
+ shadowDiv.addEventListener("mouseleave", mouseLeave, true);
+ shadowDiv.addEventListener("pointerenter", pointerEnter, true);
+ shadowDiv.addEventListener("pointerleave", pointerLeave, true);
+
+ synthesizeMouseAtCenter(lightDiv, { type: "mousemove" });
+ checkEventCounts({ mouseEnterCount: 0,
+ mouseLeaveCount: 0,
+ pointerEnterCount: 0,
+ pointerLeaveCount: 0
+ },
+ "Entered light DOM"
+ );
+
+ synthesizeMouseAtCenter(shadowDiv, { type: "mousemove" })
+ checkEventCounts({ mouseEnterCount: 2,
+ mouseLeaveCount: 0,
+ pointerEnterCount: 2,
+ pointerLeaveCount: 0
+ },
+ "Entered shadow DOM");
+
+ synthesizeMouseAtCenter(lightDiv, { type: "mousemove" })
+ checkEventCounts({ mouseEnterCount: 2,
+ mouseLeaveCount: 2,
+ pointerEnterCount: 2,
+ pointerLeaveCount: 2
+ },
+ "Left shadow DOM"
+ );
+
+ parent.SimpleTest.finish();
+ }
+
+ function lazyRequestAnimationFrame(fn) {
+ requestAnimationFrame(
+ function() {
+ setTimeout(fn);
+ });
+ }
+ </script>
+ </head>
+ <body onload="lazyRequestAnimationFrame(test)">
+ <div id="lightDiv">light DOM</div>
+ <div id="host"></div>
+ </body>
+</html>
diff --git a/dom/events/test/file_bug1692052.html b/dom/events/test/file_bug1692052.html
new file mode 100644
index 0000000000..23b26d95c7
--- /dev/null
+++ b/dom/events/test/file_bug1692052.html
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script>
+ window.addEventListener('load', () => {
+ const canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas')
+ document.adoptNode(document.documentElement)
+ document.appendChild(canvas)
+ canvas.focus()
+ canvas.contentEditable = 'true'
+ canvas.focus()
+ setTimeout(() => {
+ let params = new URLSearchParams(location.search);
+ let q = parseInt(params.get("c") || 0) + 1;
+ if (q > 5) {
+ window.opener.finishTests();
+ window.close();
+ return;
+ }
+ location.href = location.pathname + "?c=" + q;
+ }, 0)
+ })
+ </script>
+</head>
+</html>
diff --git a/dom/events/test/file_bug679494.html b/dom/events/test/file_bug679494.html
new file mode 100644
index 0000000000..a2e47916c5
--- /dev/null
+++ b/dom/events/test/file_bug679494.html
@@ -0,0 +1,8 @@
+<html>
+<head>
+ <title>Test for Bug 679494</title>
+</head>
+<body>
+ There and back again.
+</body>
+</html>
diff --git a/dom/events/test/file_coalesce_touchmove_browserchild.html b/dom/events/test/file_coalesce_touchmove_browserchild.html
new file mode 100644
index 0000000000..28a4ae5252
--- /dev/null
+++ b/dom/events/test/file_coalesce_touchmove_browserchild.html
@@ -0,0 +1,117 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
+ <title>touchmove coalescing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ var receivedSingleTouch = false;
+ var receivedMultiTouch = false;
+ // This test does three things:
+ // 1. Ensures adding a new touch to existing touchmove events
+ // will not be coalesced with the previous single touch touchmove events
+ // 2. Ensures touchstart flushes the coalesced touchmove events
+ // 3. Ensures touchend flushes the coalesced touchmove event
+ async function testBrowserChildCoalescing() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var y = Math.floor(rect.y + (rect.height / 4));
+ var endYForFirstTouch = Math.floor(rect.y + ((rect.height / 4) * 2));
+ var endYForSecondTouch = Math.floor(rect.y + ((rect.height / 4) * 4));
+ utils.sendTouchEvent("touchstart", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+ while (y != endYForFirstTouch) {
+ utils.sendTouchEvent("touchmove", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+ ++y;
+ }
+
+ // Add a new touch and move this touch
+ utils.sendTouchEvent("touchstart", [0, 1], [x, x], [endYForFirstTouch, endYForFirstTouch], [1, 1], [1, 1], [0, 0], [1, 1],
+ [0, 0], [0, 0], [0, 0], 0, false);
+ while (y != endYForSecondTouch) {
+ utils.sendTouchEvent("touchmove", [0, 1], [x, x], [endYForFirstTouch, y], [1, 1], [1, 1], [0, 0], [1, 1],
+ [0, 0], [0, 0], [0, 0], 0, false);
+ ++y;
+ }
+
+ utils.sendTouchEvent("touchend", [0, 1], [x, x], [endYForFirstTouch, endYForSecondTouch], [1, 1], [1, 1], [0, 0], [1, 1],
+ [0, 0], [0, 0], [0, 0], 0, false);
+ });
+
+ let touchStartCount = 0;
+ let shouldReceiveMultiTouch = false;
+ let receivedTouchEnd = false;
+
+ window.addEventListener("touchstart", function() {
+ ++touchStartCount;
+ if (touchStartCount == 2) {
+ shouldReceiveMultiTouch = true;
+ }
+ }, true);
+
+
+ window.addEventListener("touchmove", function(e) {
+ if (receivedTouchEnd) {
+ ok(false, "Shouldn't get any touchmove events after touchend");
+ }
+
+ // Make touchmove handling slow
+ var start = performance.now();
+ while (performance.now() < (start + 10));
+
+ if (shouldReceiveMultiTouch) {
+ is(e.touches.length, 2, "Should get two touches for multi touch");
+ receivedMultiTouch = true;
+ } else {
+ is(e.touches.length, 1, "Should get one touch for single touch");
+ receivedSingleTouch = true;
+ }
+ }, true);
+
+ window.addEventListener("touchend", async function(e) {
+ receivedTouchEnd = true;
+ // Request a tick to ensure touchend has successfully flushed
+ // coalesced touchmove events
+ await new Promise(r => {
+ window.requestAnimationFrame(() => {
+ window.requestAnimationFrame(r);
+ });
+ });
+
+ resolve();
+ }, {once: true});
+ });
+
+ return ret;
+ }
+
+ async function runTests() {
+ await SpecialPowers.pushPrefEnv({"set": [["dom.events.coalesce.touchmove", true]]});
+
+ await testBrowserChildCoalescing();
+
+ ok(receivedSingleTouch, "Should've got single touch");
+ ok(receivedMultiTouch, "Should've got multi touch");
+
+ opener.finish();
+ window.close();
+ }
+
+ function init() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", true]]},
+ runTests);
+ }
+ </script>
+</head>
+<body onload="SimpleTest.waitForFocus(init);">
+</body>
+</html>
diff --git a/dom/events/test/file_coalesce_touchmove_browserchild2.html b/dom/events/test/file_coalesce_touchmove_browserchild2.html
new file mode 100644
index 0000000000..b29ae291c2
--- /dev/null
+++ b/dom/events/test/file_coalesce_touchmove_browserchild2.html
@@ -0,0 +1,193 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
+ <title>touchmove coalescing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ window.oncontextmenu = function(e) {
+ e.preventDefault();
+ }
+
+ window.addEventListener("touchstart", function(e) { e.preventDefault(); },
+ { passive: false} );
+
+ var touchmoveEvents = [];
+ function touchmove(e) {
+ // Make touchmove handling slow
+ var start = performance.now();
+ while (performance.now() < (start + 5));
+ touchmoveEvents.push(e);
+ }
+
+ async function fireLotsOfSingleTouchMoves() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var y = Math.floor(rect.y + (rect.height / 4));
+ var endY = Math.floor(rect.y + ((rect.height / 4) * 2));
+ utils.sendTouchEvent("touchstart", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+ while (y != endY) {
+ utils.sendTouchEvent("touchmove", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+ ++y;
+ }
+ utils.sendTouchEvent("touchend", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+
+ });
+
+ touchmoveEvents = [];
+ window.addEventListener("touchmove", touchmove, true);
+ window.addEventListener("touchend", function(e) {
+ window.removeEventListener("touchmove", touchmove, true);
+ resolve(touchmoveEvents);
+ }, {once: true});
+ });
+
+ return ret
+ }
+
+ async function fireTwoSingleTouches() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var startY = Math.floor(rect.y + (rect.height / 4));
+ var endY = Math.floor(rect.y + ((rect.height / 4) * 2));
+ utils.sendTouchEvent("touchstart", [0], [x], [startY], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ utils.sendTouchEvent("touchmove", [0], [x], [startY], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ utils.sendTouchEvent("touchmove", [0], [x], [startY + 1], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ utils.sendTouchEvent("touchend", [0], [x], [endY], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ });
+
+ touchmoveEvents = [];
+ window.addEventListener("touchmove", touchmove, true);
+ window.addEventListener("touchend", function(e) {
+ window.removeEventListener("touchmove", touchmove, true);
+ resolve(touchmoveEvents);
+ }, {once: true});
+ });
+
+ return ret
+ }
+
+ async function fireLotsOfMultiTouchMoves() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var startY = Math.floor(rect.y + (rect.height / 4));
+ var endY = Math.floor(rect.y + ((rect.height / 4) * 2));
+ utils.sendTouchEvent("touchstart", [0, 1], [x, x + 1],
+ [startY, startY + 1], [1, 1], [1, 1], [0, 0],
+ [1, 1], [0, 0], [0, 0], [0, 0], 0, false);
+ while (startY != endY) {
+ utils.sendTouchEvent("touchmove", [0, 1], [x, x + 1],
+ [startY, startY + 1], [1, 1], [1, 1], [0, 0],
+ [1, 1], [0, 0], [0, 0], [0, 0], 0, false);
+ ++startY;
+ }
+ utils.sendTouchEvent("touchend", [0, 1], [x, x + 1], [endY, endY + 1],
+ [1, 1], [1, 1], [0, 0], [1, 1], [0, 0], [0, 0],
+ [0, 0], 0, false);
+
+ });
+
+ touchmoveEvents = [];
+ window.addEventListener("touchmove", touchmove, true);
+ window.addEventListener("touchend", function(e) {
+ window.removeEventListener("touchmove", touchmove, true);
+ resolve(touchmoveEvents);
+ }, {once: true});
+ });
+
+ return ret
+ }
+
+ function disableCoalescing() {
+ return SpecialPowers.pushPrefEnv({"set": [["dom.events.compress.touchmove",
+ false],
+ ["dom.events.coalesce.touchmove",
+ false]]});
+ }
+
+ function enableCoalescing() {
+ return SpecialPowers.pushPrefEnv({"set": [["dom.events.compress.touchmove",
+ false],
+ ["dom.events.coalesce.touchmove",
+ true]]});
+ }
+
+ async function runTests() {
+ document.body.clientTop; // Flush layout
+
+ await disableCoalescing();
+ var touchMovesWithoutCoalescing = await fireLotsOfSingleTouchMoves();
+ await enableCoalescing();
+ var touchMovesWithCoalescing = await fireLotsOfSingleTouchMoves();
+ opener.ok(touchMovesWithoutCoalescing.length > touchMovesWithCoalescing.length,
+ "Coalescing should reduce the number of touchmove events");
+ opener.isDeeply(touchMovesWithoutCoalescing.shift().touches,
+ touchMovesWithCoalescing.shift().touches,
+ "Shouldn't have coalesced the initial touchmove");
+ opener.isDeeply(touchMovesWithoutCoalescing.pop().touches,
+ touchMovesWithCoalescing.pop().touches,
+ "The last touchmove event should be up-to-date");
+
+ await disableCoalescing();
+ var twoTouchMovesWithoutCoalescing = await fireTwoSingleTouches();
+ await enableCoalescing();
+ var twoTouchMovesWithCoalescing = await fireTwoSingleTouches();
+ opener.is(twoTouchMovesWithoutCoalescing.length, 2,
+ "Should have got two touchmoves");
+ opener.is(twoTouchMovesWithoutCoalescing.length,
+ twoTouchMovesWithCoalescing.length,
+ "Shouldn't have coalesced the touchmove.");
+
+ await disableCoalescing();
+ var multiTouchMovesWithoutCoalescing = await fireLotsOfMultiTouchMoves();
+ await enableCoalescing();
+ var multiTouchMovesWithCoalescing = await fireLotsOfMultiTouchMoves();
+ opener.ok(multiTouchMovesWithoutCoalescing.length > multiTouchMovesWithCoalescing.length,
+ "Coalescing should reduce the number of multitouch touchmove events");
+ opener.isDeeply(multiTouchMovesWithoutCoalescing.shift().touches,
+ multiTouchMovesWithCoalescing.shift().touches,
+ "Shouldn't have coalesced the initial multitouch touchmove event");
+ opener.isDeeply(multiTouchMovesWithoutCoalescing.pop().touches,
+ multiTouchMovesWithCoalescing.pop().touches,
+ "The last multitouch touchmove event should be up-to-date");
+
+ opener.finish();
+ window.close();
+ }
+
+ function init() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", true]]},
+ runTests);
+ }
+ </script>
+</head>
+<body style="height: 100vh;" onload="SimpleTest.waitForFocus(init);">
+</body>
+</html>
diff --git a/dom/events/test/file_coalesce_touchmove_ipc.html b/dom/events/test/file_coalesce_touchmove_ipc.html
new file mode 100644
index 0000000000..73e5a1157d
--- /dev/null
+++ b/dom/events/test/file_coalesce_touchmove_ipc.html
@@ -0,0 +1,193 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1">
+ <title>touchmove coalescing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ window.oncontextmenu = function(e) {
+ e.preventDefault();
+ }
+
+ window.addEventListener("touchstart", function(e) { e.preventDefault(); },
+ { passive: false} );
+
+ var touchmoveEvents = [];
+ function touchmove(e) {
+ // Make touchmove handling slow
+ var start = performance.now();
+ while (performance.now() < (start + 10));
+ touchmoveEvents.push(e);
+ }
+
+ async function fireLotsOfSingleTouchMoves() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var y = Math.floor(rect.y + (rect.height / 4));
+ var endY = Math.floor(rect.y + ((rect.height / 4) * 2));
+ utils.sendTouchEvent("touchstart", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+ while (y != endY) {
+ utils.sendTouchEvent("touchmove", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+ ++y;
+ }
+ utils.sendTouchEvent("touchend", [0], [x], [y], [1], [1], [0], [1],
+ [0], [0], [0], 0, false);
+
+ });
+
+ touchmoveEvents = [];
+ window.addEventListener("touchmove", touchmove, true);
+ window.addEventListener("touchend", function(e) {
+ window.removeEventListener("touchmove", touchmove, true);
+ resolve(touchmoveEvents);
+ }, {once: true});
+ });
+
+ return ret
+ }
+
+ async function fireTwoSingleTouches() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var startY = Math.floor(rect.y + (rect.height / 4));
+ var endY = Math.floor(rect.y + ((rect.height / 4) * 2));
+ utils.sendTouchEvent("touchstart", [0], [x], [startY], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ utils.sendTouchEvent("touchmove", [0], [x], [startY], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ utils.sendTouchEvent("touchmove", [0], [x], [startY + 1], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ utils.sendTouchEvent("touchend", [0], [x], [endY], [1], [1], [0],
+ [1], [0], [0], [0], 0, false);
+ });
+
+ touchmoveEvents = [];
+ window.addEventListener("touchmove", touchmove, true);
+ window.addEventListener("touchend", function(e) {
+ window.removeEventListener("touchmove", touchmove, true);
+ resolve(touchmoveEvents);
+ }, {once: true});
+ });
+
+ return ret
+ }
+
+ async function fireLotsOfMultiTouchMoves() {
+ var ret = new Promise(function(resolve) {
+ SpecialPowers.loadChromeScript(function() {
+ /* eslint-env mozilla/chrome-script */
+ var element = this.actorParent.rootFrameLoader.ownerElement;
+ var rect = element.getBoundingClientRect();
+ var win = element.ownerDocument.defaultView;
+ var utils = win.windowUtils;
+ var x = rect.x + (rect.width / 2);
+ var startY = Math.floor(rect.y + (rect.height / 4));
+ var endY = Math.floor(rect.y + ((rect.height / 4) * 2));
+ utils.sendTouchEvent("touchstart", [0, 1], [x, x + 1],
+ [startY, startY + 1], [1, 1], [1, 1], [0, 0],
+ [1, 1], [0, 0], [0, 0], [0, 0], 0, false);
+ while (startY != endY) {
+ utils.sendTouchEvent("touchmove", [0, 1], [x, x + 1],
+ [startY, startY + 1], [1, 1], [1, 1], [0, 0],
+ [1, 1], [0, 0], [0, 0], [0, 0], 0, false);
+ ++startY;
+ }
+ utils.sendTouchEvent("touchend", [0, 1], [x, x + 1], [endY, endY + 1],
+ [1, 1], [1, 1], [0, 0], [1, 1], [0, 0], [0, 0],
+ [0, 0], 0, false);
+
+ });
+
+ touchmoveEvents = [];
+ window.addEventListener("touchmove", touchmove, true);
+ window.addEventListener("touchend", function(e) {
+ window.removeEventListener("touchmove", touchmove, true);
+ resolve(touchmoveEvents);
+ }, {once: true});
+ });
+
+ return ret
+ }
+
+ function disableIPCCompression() {
+ return SpecialPowers.pushPrefEnv({"set": [["dom.events.compress.touchmove",
+ false],
+ ["dom.events.coalesce.touchmove",
+ false]]});
+ }
+
+ function enableIPCCompression() {
+ return SpecialPowers.pushPrefEnv({"set": [["dom.events.compress.touchmove",
+ true],
+ ["dom.events.coalesce.touchmove",
+ false]]});
+ }
+
+ async function runTests() {
+ document.body.clientTop; // Flush layout
+
+ await disableIPCCompression();
+ var touchMovesWithoutCoalescing = await fireLotsOfSingleTouchMoves();
+ await enableIPCCompression();
+ var touchMovesWithCoalescing = await fireLotsOfSingleTouchMoves();
+ opener.ok(touchMovesWithoutCoalescing.length > touchMovesWithCoalescing.length,
+ "Coalescing should reduce the number of touchmove events");
+ opener.isDeeply(touchMovesWithoutCoalescing.shift().touches,
+ touchMovesWithCoalescing.shift().touches,
+ "Shouldn't have coalesced the initial touchmove");
+ opener.isDeeply(touchMovesWithoutCoalescing.pop().touches,
+ touchMovesWithCoalescing.pop().touches,
+ "The last touchmove event should be up-to-date");
+
+ await disableIPCCompression();
+ var twoTouchMovesWithoutCoalescing = await fireTwoSingleTouches();
+ await enableIPCCompression();
+ var twoTouchMovesWithCoalescing = await fireTwoSingleTouches();
+ opener.is(twoTouchMovesWithoutCoalescing.length, 2,
+ "Should have got two touchmoves");
+ opener.is(twoTouchMovesWithoutCoalescing.length,
+ twoTouchMovesWithCoalescing.length,
+ "Shouldn't have coalesced the touchmove.");
+
+ await disableIPCCompression();
+ var multiTouchMovesWithoutCoalescing = await fireLotsOfMultiTouchMoves();
+ await enableIPCCompression();
+ var multiTouchMovesWithCoalescing = await fireLotsOfMultiTouchMoves();
+ opener.ok(multiTouchMovesWithoutCoalescing.length > multiTouchMovesWithCoalescing.length,
+ "Coalescing should reduce the number of multitouch touchmove events");
+ opener.isDeeply(multiTouchMovesWithoutCoalescing.shift().touches,
+ multiTouchMovesWithCoalescing.shift().touches,
+ "Shouldn't have coalesced the initial multitouch touchmove event");
+ opener.isDeeply(multiTouchMovesWithoutCoalescing.pop().touches,
+ multiTouchMovesWithCoalescing.pop().touches,
+ "The last multitouch touchmove event should be up-to-date");
+
+ opener.finish();
+ window.close();
+ }
+
+ function init() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", true]]},
+ runTests);
+ }
+ </script>
+</head>
+<body style="height: 100vh;" onload="SimpleTest.waitForFocus(init);">
+</body>
+</html>
diff --git a/dom/events/test/file_empty.html b/dom/events/test/file_empty.html
new file mode 100644
index 0000000000..e75bdc942f
--- /dev/null
+++ b/dom/events/test/file_empty.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script>
+window.addEventListener("load", async function() {
+ await promiseApzFlushedRepaints();
+ parent.postMessage("ready", "*");
+}, { once: true });
+</script>
+</head>
+<body>
+</body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/file_event_screenXY.html b/dom/events/test/file_event_screenXY.html
new file mode 100644
index 0000000000..d39e28abc2
--- /dev/null
+++ b/dom/events/test/file_event_screenXY.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<style>
+html, body {
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+}
+</style>
+<div style="width:100%;height:100%;background-color:red;"></div>
+<script>
+ document.querySelector("div").addEventListener("click", event => {
+ parent.postMessage({ screenX: event.screenX,
+ screenY: event.screenY,
+ clientX: event.clientX,
+ clientY: event.clientY }, "*");
+ });
+ window.onload = async () => {
+ await promiseApzFlushedRepaints();
+ parent.postMessage("ready", "*");
+ }
+</script>
diff --git a/dom/events/test/file_focus_blur_on_click_in_cross_origin_iframe.html b/dom/events/test/file_focus_blur_on_click_in_cross_origin_iframe.html
new file mode 100644
index 0000000000..09eab8fc16
--- /dev/null
+++ b/dom/events/test/file_focus_blur_on_click_in_cross_origin_iframe.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<style>
+html, body {
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+}
+</style>
+<div style="width:100%;height:100%;background-color:blue;"></div>
+<script>
+ document.querySelector("div").addEventListener("click", event => {
+ parent.postMessage("click", "*");
+ });
+ window.onload = async () => {
+ // Wait for APZ state stable so that mouse event handling APZ works properly
+ // in out-of-process iframes.
+ await promiseApzFlushedRepaints();
+ parent.postMessage("ready", "*");
+ };
+ document.body.onfocus = () => {
+ parent.postMessage("focus", "*");
+ };
+ document.body.onblur = () => {
+ parent.postMessage("blur", "*");
+ };
+</script>
diff --git a/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_inner.html b/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_inner.html
new file mode 100644
index 0000000000..e7127e2397
--- /dev/null
+++ b/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_inner.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<style>
+html, body {
+ height: 100%;
+ margin: 0px;
+ padding: 0px;
+}
+</style>
+<div style="width:100%;height:100%;background-color:blue;"></div>
+<script>
+ document.querySelector("div").addEventListener("click", event => {
+ parent.postMessage("innerclick", "*");
+ });
+ window.onload = async () => {
+ // Wait for APZ state stable so that mouse event handling APZ works properly
+ // in out-of-process iframes.
+ await promiseApzFlushedRepaints();
+ parent.postMessage("innerready", "*");
+ };
+ document.body.onfocus = () => {
+ parent.postMessage("innerfocus", "*");
+ };
+ document.body.onblur = () => {
+ parent.postMessage("innerblur", "*");
+ };
+</script>
diff --git a/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_middle.html b/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_middle.html
new file mode 100644
index 0000000000..bb128422ae
--- /dev/null
+++ b/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_middle.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<style>
+html, body {
+ height: 200px;
+ margin: 0px;
+ padding: 0px;
+}
+</style>
+<body>
+<script>
+ window.addEventListener("message", async (event) => {
+ if (event.data == "innerready") {
+ // Wait for APZ state stable so that mouse event handling APZ works properly
+ // in out-of-process iframes.
+ await promiseApzFlushedRepaints();
+ }
+ parent.postMessage(event.data, "*");
+ });
+ window.onload = () => {
+ parent.postMessage("middleready", "*");
+ };
+ document.body.onfocus = () => {
+ parent.postMessage("middlefocus", "*");
+ };
+ document.body.onblur = () => {
+ parent.postMessage("middleblur", "*");
+ };
+</script>
+<div style="width:100px;height:100px;background-color:gray;"></div>
+<iframe width="100" height="100" src="https://example.org/tests/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_inner.html"></iframe>
+<script>
+ document.querySelector("div").addEventListener("click", event => {
+ parent.postMessage("middleclick", "*");
+ });
+</script>
diff --git a/dom/events/test/file_keyboard_event_init_key_event_enabled_in_contentscript.html b/dom/events/test/file_keyboard_event_init_key_event_enabled_in_contentscript.html
new file mode 100644
index 0000000000..98691bfe69
--- /dev/null
+++ b/dom/events/test/file_keyboard_event_init_key_event_enabled_in_contentscript.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Storing type of `KeyboardEvent.initKeyEvent`</title>
+<script>
+document.documentElement.setAttribute(
+ "data-initKeyEvent-before",
+ typeof window.KeyboardEvent.prototype.initKeyEvent
+);
+window.addEventListener("load", () => {
+ document.documentElement.setAttribute(
+ "data-initKeyEvent-after",
+ typeof window.KeyboardEvent.prototype.initKeyEvent
+ );
+}, { capture: false, once: true });
+</script>
+</head>
+<body>
+</body>
+</html>
diff --git a/dom/events/test/file_mouse_enterleave.html b/dom/events/test/file_mouse_enterleave.html
new file mode 100644
index 0000000000..2a5bc743f7
--- /dev/null
+++ b/dom/events/test/file_mouse_enterleave.html
@@ -0,0 +1,45 @@
+<html>
+<body>
+<style>
+#target {
+ width: 100%;
+ height: 100%;
+}
+#reflow {
+ width: 100%;
+ height: 10px;
+ background-color: red;
+}
+</style>
+<div id="target"></div>
+<div id="reflow"></div>
+<script>
+function listener(e) {
+ parent.postMessage({ eventType: e.type, targetName: e.target.localName }, "*");
+}
+
+window.addEventListener("message", function(aEvent) {
+ if (aEvent.data === "reflow") {
+ let reflow = document.getElementById("reflow");
+ reflow.style.display = "none";
+ reflow.getBoundingClientRect();
+ reflow.style.display = "block";
+ reflow.getBoundingClientRect();
+
+ // Now wait a bit to see if there is any unexpected events fired.
+ requestAnimationFrame(
+ () => parent.postMessage({ eventType: "reflowed" }, "*")
+ );
+ }
+});
+
+let target = document.getElementById("target");
+target.addEventListener("mouseenter", listener);
+target.addEventListener("mouseleave", listener);
+
+let root = document.documentElement;
+root.addEventListener("mouseenter", listener);
+root.addEventListener("mouseleave", listener);
+</script>
+</body>
+</html>
diff --git a/dom/events/test/green.png b/dom/events/test/green.png
new file mode 100644
index 0000000000..7df25f33bd
--- /dev/null
+++ b/dom/events/test/green.png
Binary files differ
diff --git a/dom/events/test/gtest/TestShortcutKeyDefinitions.cpp b/dom/events/test/gtest/TestShortcutKeyDefinitions.cpp
new file mode 100644
index 0000000000..10adbba49f
--- /dev/null
+++ b/dom/events/test/gtest/TestShortcutKeyDefinitions.cpp
@@ -0,0 +1,634 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+
+#include "KeyEventHandler.h"
+#include "ShortcutKeys.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/dom/KeyboardEvent.h"
+
+namespace mozilla {
+
+/**
+ * These tests checks whether the key mapping defined by
+ * `ShortcutKeyDefinitions.cpp` and its including header files.
+ * Note that these tests do NOT define how shortcut keys mapped. These tests
+ * exist for checking unexpected change occurs by cleaning up or changing
+ * the mapping.
+ */
+
+#ifndef MOZ_WIDGET_COCOA
+# define MODIFIER_ACCEL MODIFIER_CONTROL
+#else
+# define MODIFIER_ACCEL MODIFIER_META
+#endif
+
+struct ShortcutKeyMap final {
+ uint32_t const mKeyCode;
+ char32_t const mCharCode;
+ Modifiers const mModifiers;
+ const char16ptr_t mCommandWin;
+ const char16ptr_t mCommandMac;
+ const char16ptr_t mCommandLinux;
+ const char16ptr_t mCommandAndroid;
+ const char16ptr_t mCommandEmacs;
+};
+
+#if defined(XP_WIN)
+# define GetCommandForPlatform(aMap) aMap.mCommandWin
+#elif defined(MOZ_WIDGET_COCOA)
+# define GetCommandForPlatform(aMap) aMap.mCommandMac
+#elif defined(MOZ_WIDGET_GTK)
+# define GetCommandForPlatform(aMap) aMap.mCommandLinux
+#elif defined(MOZ_WIDGET_ANDROID)
+# define GetCommandForPlatform(aMap) aMap.mCommandAndroid
+#else
+# define GetCommandForPlatform(aMap) aMap.mCommandEmacs
+#endif
+
+static bool GetCommandFor(KeyEventHandler* aFirstHandler,
+ dom::KeyboardEvent* aDOMEvent, nsAString& aCommand) {
+ if (!aFirstHandler) {
+ return false;
+ }
+ aCommand.Truncate();
+ for (KeyEventHandler* handler = aFirstHandler; handler;
+ handler = handler->GetNextHandler()) {
+ if (handler->KeyEventMatched(aDOMEvent, 0, IgnoreModifierState())) {
+ handler->GetCommand(aCommand);
+ return true;
+ }
+ }
+ return false;
+}
+
+static char16ptr_t GetExpectedCommandFor(
+ const nsTArray<ShortcutKeyMap>& aExpectedMap,
+ const WidgetKeyboardEvent& aWidgetEvent) {
+ MOZ_ASSERT(!aExpectedMap.IsEmpty());
+ for (const ShortcutKeyMap& map : aExpectedMap) {
+ if (aWidgetEvent.mKeyCode == map.mKeyCode &&
+ aWidgetEvent.mCharCode == map.mCharCode &&
+ aWidgetEvent.mModifiers == map.mModifiers) {
+ return GetCommandForPlatform(map);
+ }
+ }
+ return nullptr;
+}
+
+struct KeyCodeAndStr final {
+ uint32_t mKeyCode;
+ KeyNameIndex mKeyNameIndex;
+ const char* mStr;
+};
+static const KeyCodeAndStr kKeyCodes[] = {
+ {NS_VK_BACK, KEY_NAME_INDEX_Backspace, "Backspace"},
+ {NS_VK_RETURN, KEY_NAME_INDEX_Enter, "Enter"},
+ {NS_VK_ESCAPE, KEY_NAME_INDEX_Escape, "Escape"},
+ {NS_VK_PAGE_UP, KEY_NAME_INDEX_PageUp, "PageUp"},
+ {NS_VK_PAGE_DOWN, KEY_NAME_INDEX_PageDown, "PageDown"},
+ {NS_VK_END, KEY_NAME_INDEX_End, "End"},
+ {NS_VK_HOME, KEY_NAME_INDEX_Home, "Home"},
+ {NS_VK_LEFT, KEY_NAME_INDEX_ArrowLeft, "ArrowLeft"},
+ {NS_VK_UP, KEY_NAME_INDEX_ArrowUp, "ArrowUp"},
+ {NS_VK_RIGHT, KEY_NAME_INDEX_ArrowRight, "ArrowRight"},
+ {NS_VK_DOWN, KEY_NAME_INDEX_ArrowDown, "ArrowDown"},
+#ifndef MOZ_WIDGET_COCOA
+ // No Insert key on macOS
+ {NS_VK_INSERT, KEY_NAME_INDEX_Insert, "Insert"},
+#endif
+ {NS_VK_DELETE, KEY_NAME_INDEX_Delete, "Delete"},
+ {NS_VK_F1, KEY_NAME_INDEX_F1, "F1"},
+ {NS_VK_F2, KEY_NAME_INDEX_F2, "F2"},
+ {NS_VK_F3, KEY_NAME_INDEX_F3, "F3"},
+ {NS_VK_F4, KEY_NAME_INDEX_F4, "F4"},
+ {NS_VK_F5, KEY_NAME_INDEX_F5, "F5"},
+ {NS_VK_F6, KEY_NAME_INDEX_F6, "F6"},
+ {NS_VK_F7, KEY_NAME_INDEX_F7, "F7"},
+ {NS_VK_F8, KEY_NAME_INDEX_F8, "F8"},
+ {NS_VK_F9, KEY_NAME_INDEX_F9, "F9"},
+ {NS_VK_F10, KEY_NAME_INDEX_F10, "F10"},
+ {NS_VK_F11, KEY_NAME_INDEX_F11, "F11"},
+ {NS_VK_F12, KEY_NAME_INDEX_F12, "F12"},
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_ANDROID)
+ {NS_VK_F13, KEY_NAME_INDEX_F13, "F13"},
+ {NS_VK_F14, KEY_NAME_INDEX_F14, "F14"},
+ {NS_VK_F15, KEY_NAME_INDEX_F15, "F15"},
+ {NS_VK_F16, KEY_NAME_INDEX_F16, "F16"},
+ {NS_VK_F17, KEY_NAME_INDEX_F17, "F17"},
+ {NS_VK_F18, KEY_NAME_INDEX_F18, "F18"},
+ {NS_VK_F19, KEY_NAME_INDEX_F19, "F19"},
+#endif
+#if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
+ {NS_VK_F20, KEY_NAME_INDEX_F20, "F20"},
+ {NS_VK_F21, KEY_NAME_INDEX_F21, "F21"},
+ {NS_VK_F22, KEY_NAME_INDEX_F22, "F22"},
+ {NS_VK_F23, KEY_NAME_INDEX_F23, "F23"},
+ {NS_VK_F24, KEY_NAME_INDEX_F24, "F24"},
+#endif
+};
+
+static const unsigned char kCharCodes[] = {
+ ' ', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
+ 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y',
+ 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
+
+struct ModifiersAndStr final {
+ Modifiers mModifiers;
+ const char* mStr;
+};
+static const ModifiersAndStr kModifiers[] = {
+ {MODIFIER_NONE, ""},
+ {MODIFIER_SHIFT, "Shift+"},
+ {MODIFIER_CONTROL, "Control+"},
+ {MODIFIER_CONTROL | MODIFIER_SHIFT, "Contrl+Shift+"},
+ {MODIFIER_ALT, "Alt+"},
+ {MODIFIER_ALT | MODIFIER_SHIFT, "Alt+Shift+"},
+ {MODIFIER_META, "Meta+"},
+ {MODIFIER_META | MODIFIER_SHIFT, "Meta+Shift+"},
+ {MODIFIER_META | MODIFIER_ALT | MODIFIER_SHIFT, "Meta+Alt+Shift+"},
+ {MODIFIER_CONTROL | MODIFIER_ALT, "Control+Alt+"},
+ {MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_SHIFT, "Control+Alt+Shift+"},
+};
+
+static void TestAllKeyCodes(KeyEventHandler* aFirstHandler,
+ const nsTArray<ShortcutKeyMap>& aExpectedMap) {
+ WidgetKeyboardEvent widgetEvent(true, eKeyPress, nullptr);
+ RefPtr<dom::KeyboardEvent> domEvent =
+ NS_NewDOMKeyboardEvent(nullptr, nullptr, &widgetEvent);
+ nsAutoString command;
+ uint32_t foundCommand = 0;
+ for (const auto& mod : kModifiers) {
+ widgetEvent.mModifiers = mod.mModifiers;
+ for (const auto& keyCode : kKeyCodes) {
+ widgetEvent.mKeyCode = keyCode.mKeyCode;
+ widgetEvent.mKeyNameIndex = keyCode.mKeyNameIndex;
+ const char16ptr_t expectedCommand =
+ GetExpectedCommandFor(aExpectedMap, widgetEvent);
+ if (GetCommandFor(aFirstHandler, domEvent, command)) {
+ foundCommand++;
+ if (expectedCommand) {
+ ASSERT_TRUE(command.Equals(expectedCommand))
+ << mod.mStr << keyCode.mStr << ": got "
+ << NS_ConvertUTF16toUTF8(command).get() << ", but expected "
+ << NS_ConvertUTF16toUTF8(expectedCommand).get();
+ } else {
+ ASSERT_TRUE(false)
+ << mod.mStr << keyCode.mStr << ": got "
+ << NS_ConvertUTF16toUTF8(command).get()
+ << ", but expected no command";
+ }
+ } else if (expectedCommand) {
+ ASSERT_TRUE(false)
+ << mod.mStr << keyCode.mStr << ": got no command, but expected "
+ << NS_ConvertUTF16toUTF8(expectedCommand).get();
+ }
+ }
+ widgetEvent.mKeyCode = 0;
+ widgetEvent.mKeyNameIndex = KEY_NAME_INDEX_USE_STRING;
+ for (unsigned char charCode : kCharCodes) {
+ widgetEvent.mCharCode = charCode;
+ widgetEvent.mKeyValue.Assign(charCode);
+ const char16ptr_t expectedCommand =
+ GetExpectedCommandFor(aExpectedMap, widgetEvent);
+ if (GetCommandFor(aFirstHandler, domEvent, command)) {
+ foundCommand++;
+ if (expectedCommand) {
+ ASSERT_TRUE(command.Equals(expectedCommand))
+ << mod.mStr << "'" << nsAutoCString(charCode).get() << "': got "
+ << NS_ConvertUTF16toUTF8(command).get() << ", but expected "
+ << NS_ConvertUTF16toUTF8(expectedCommand).get();
+ } else {
+ ASSERT_TRUE(false)
+ << mod.mStr << "'" << nsAutoCString(charCode).get() << "': got "
+ << NS_ConvertUTF16toUTF8(command).get()
+ << ", but expected no command";
+ }
+ } else if (expectedCommand) {
+ ASSERT_TRUE(false)
+ << mod.mStr << "'" << nsAutoCString(charCode).get()
+ << "': got no command, but expected "
+ << NS_ConvertUTF16toUTF8(expectedCommand).get();
+ }
+ }
+ widgetEvent.mCharCode = 0;
+ widgetEvent.mKeyValue.Truncate();
+ }
+ uint32_t expectedCommandCount = 0;
+ for (const auto& map : aExpectedMap) {
+ if (GetCommandForPlatform(map)) {
+ expectedCommandCount++;
+ }
+ }
+ ASSERT_EQ(foundCommand, expectedCommandCount)
+ << "Some expected shortcut keys have not been tested";
+ uint32_t countOfHandler = 0;
+ for (KeyEventHandler* handler = aFirstHandler; handler;
+ handler = handler->GetNextHandler()) {
+ countOfHandler++;
+ }
+ ASSERT_EQ(countOfHandler, expectedCommandCount)
+ << "Some unnecessary key handlers found";
+}
+
+TEST(ShortcutKeyDefinitions, HTMLInputElement)
+{
+ // clang-format off
+ const nsTArray<ShortcutKeyMap> expectedMap{
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_NONE, u"cmd_moveLeft", nullptr, u"cmd_moveLeft", u"cmd_moveLeft", u"cmd_moveLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_NONE, u"cmd_moveRight", nullptr, u"cmd_moveRight", u"cmd_moveRight", u"cmd_moveRight"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT, u"cmd_selectLeft", nullptr, u"cmd_selectLeft", u"cmd_selectLeft", u"cmd_selectLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT, u"cmd_selectRight", nullptr, u"cmd_selectRight", u"cmd_selectRight", u"cmd_selectRight"},
+ {NS_VK_LEFT, 0, MODIFIER_CONTROL, u"cmd_moveLeft2", nullptr, nullptr, u"cmd_wordPrevious", u"cmd_wordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_CONTROL, u"cmd_moveRight2", nullptr, nullptr, u"cmd_wordNext", u"cmd_wordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_beginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_endLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_NONE, u"cmd_moveUp", nullptr, u"cmd_moveUp", u"cmd_moveUp", u"cmd_moveUp"},
+ {NS_VK_DOWN, 0, MODIFIER_NONE, u"cmd_moveDown", nullptr, u"cmd_moveDown", u"cmd_moveDown", u"cmd_moveDown"},
+ {NS_VK_UP, 0, MODIFIER_SHIFT, u"cmd_selectUp", nullptr, u"cmd_selectUp", u"cmd_selectUp", u"cmd_selectUp"},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectDown", nullptr, u"cmd_selectDown", u"cmd_selectDown", u"cmd_selectDown"},
+ {NS_VK_UP, 0, MODIFIER_CONTROL, u"cmd_moveUp2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_CONTROL, u"cmd_moveDown2", nullptr, nullptr, nullptr, nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectLeft2", nullptr, nullptr, u"cmd_selectWordPrevious", u"cmd_selectWordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectRight2", nullptr, nullptr, u"cmd_selectWordNext", u"cmd_selectWordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBeginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectEndLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectUp2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectDown2", nullptr, nullptr, nullptr, nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_NONE, u"cmd_beginLine", nullptr, nullptr, u"cmd_beginLine", u"cmd_beginLine"},
+ {NS_VK_END, 0, MODIFIER_NONE, u"cmd_endLine", nullptr, nullptr, u"cmd_endLine", u"cmd_endLine"},
+ {NS_VK_HOME, 0, MODIFIER_SHIFT, u"cmd_selectBeginLine", nullptr, nullptr, u"cmd_selectBeginLine", u"cmd_selectBeginLine"},
+ {NS_VK_END, 0, MODIFIER_SHIFT, u"cmd_selectEndLine", nullptr, nullptr, u"cmd_selectEndLine", u"cmd_selectEndLine"},
+ {NS_VK_HOME, 0, MODIFIER_CONTROL, u"cmd_moveTop", nullptr, nullptr, nullptr, u"cmd_beginLine"},
+ {NS_VK_END, 0, MODIFIER_CONTROL, u"cmd_moveBottom", nullptr, nullptr, nullptr, u"cmd_endLine"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectTop", nullptr, nullptr, nullptr, u"cmd_selectBeginLine"},
+ {NS_VK_END, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectBottom", nullptr, nullptr, nullptr, u"cmd_selectEndLine"},
+
+ // KeyCode Modifiers Windows macOS Linux Android
+ {NS_VK_INSERT, 0, MODIFIER_SHIFT, u"cmd_paste", nullptr, nullptr, nullptr, u"cmd_paste"},
+ {NS_VK_INSERT, 0, MODIFIER_CONTROL, u"cmd_copy", nullptr, nullptr, nullptr, u"cmd_copy"},
+ {NS_VK_DELETE, 0, MODIFIER_SHIFT, u"cmd_cutOrDelete", nullptr, nullptr, nullptr, u"cmd_cutOrDelete"},
+ {NS_VK_DELETE, 0, MODIFIER_CONTROL, u"cmd_deleteWordForward", nullptr, nullptr, u"cmd_deleteWordForward", u"cmd_copyOrDelete"},
+ {NS_VK_DELETE, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine", nullptr},
+ {NS_VK_BACK, 0, MODIFIER_CONTROL, u"cmd_deleteWordBackward", nullptr, nullptr, u"cmd_deleteWordBackward", u"cmd_deleteWordBackward"},
+ {NS_VK_BACK, 0, MODIFIER_ALT, u"cmd_undo", nullptr, nullptr, u"cmd_deleteToBeginningOfLine", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_BACK, 0, MODIFIER_SHIFT | MODIFIER_ALT, u"cmd_redo", nullptr, nullptr, nullptr, nullptr},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, 'a', MODIFIER_ACCEL, u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", nullptr},
+ {0, 'a', MODIFIER_ALT, nullptr, nullptr, nullptr, nullptr, u"cmd_selectAll"},
+ {0, 'c', MODIFIER_ACCEL, u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy"},
+ {0, 'x', MODIFIER_ACCEL, u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut"},
+ {0, 'v', MODIFIER_ACCEL, u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste"},
+ {0, 'y', MODIFIER_ACCEL, u"cmd_redo", nullptr, u"cmd_redo", nullptr, u"cmd_redo"},
+ {0, 'z', MODIFIER_ACCEL, u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo"},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste"},
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ALT | MODIFIER_ACCEL, nullptr, u"cmd_paste", nullptr, nullptr, nullptr},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'z', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo"},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'a', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_beginLine"},
+ {0, 'b', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_charPrevious"},
+ {0, 'd', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteCharForward"},
+ {0, 'e', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_endLine"},
+ {0, 'f', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_charNext"},
+ {0, 'h', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteCharBackward"},
+ {0, 'k', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine"},
+ {0, 'u', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteToBeginningOfLine"},
+ {0, 'w', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteWordBackward"},
+ };
+ // clang-format on
+
+ TestAllKeyCodes(ShortcutKeys::GetHandlers(HandlerType::eInput), expectedMap);
+
+ ShortcutKeys::Shutdown(); // Destroy the singleton instance.
+}
+
+TEST(ShortcutKeyDefinitions, HTMLTextAreaElement)
+{
+ // clang-format off
+ const nsTArray<ShortcutKeyMap> expectedMap{
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_NONE, u"cmd_moveLeft", nullptr, u"cmd_moveLeft", u"cmd_moveLeft", u"cmd_moveLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_NONE, u"cmd_moveRight", nullptr, u"cmd_moveRight", u"cmd_moveRight", u"cmd_moveRight"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT, u"cmd_selectLeft", nullptr, u"cmd_selectLeft", u"cmd_selectLeft", u"cmd_selectLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT, u"cmd_selectRight", nullptr, u"cmd_selectRight", u"cmd_selectRight", u"cmd_selectRight"},
+ {NS_VK_LEFT, 0, MODIFIER_CONTROL, u"cmd_moveLeft2", nullptr, nullptr, u"cmd_wordPrevious", u"cmd_wordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_CONTROL, u"cmd_moveRight2", nullptr, nullptr, u"cmd_wordNext", u"cmd_wordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_beginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_endLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_NONE, u"cmd_moveUp", nullptr, u"cmd_moveUp", u"cmd_moveUp", u"cmd_moveUp"},
+ {NS_VK_DOWN, 0, MODIFIER_NONE, u"cmd_moveDown", nullptr, u"cmd_moveDown", u"cmd_moveDown", u"cmd_moveDown"},
+ {NS_VK_UP, 0, MODIFIER_SHIFT, u"cmd_selectUp", nullptr, u"cmd_selectUp", u"cmd_selectUp", u"cmd_selectUp"},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectDown", nullptr, u"cmd_selectDown", u"cmd_selectDown", u"cmd_selectDown"},
+ {NS_VK_UP, 0, MODIFIER_CONTROL, u"cmd_moveUp2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_CONTROL, u"cmd_moveDown2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_UP, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveTop", nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectLeft2", nullptr, nullptr, u"cmd_selectWordPrevious", u"cmd_selectWordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectRight2", nullptr, nullptr, u"cmd_selectWordNext", u"cmd_selectWordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBeginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectEndLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectUp2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectDown2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectTop", nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_PAGE_UP, 0, MODIFIER_NONE, u"cmd_movePageUp", nullptr, nullptr, u"cmd_movePageUp", u"cmd_movePageUp"},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_NONE, u"cmd_movePageDown", nullptr, nullptr, u"cmd_movePageDown", u"cmd_movePageDown"},
+ {NS_VK_PAGE_UP, 0, MODIFIER_SHIFT, u"cmd_selectPageUp", nullptr, nullptr, u"cmd_selectPageUp", u"cmd_selectPageUp"},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectPageDown", nullptr, nullptr, u"cmd_selectPageDown", u"cmd_selectPageDown"},
+ {NS_VK_PAGE_UP, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveTop", nullptr},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_PAGE_UP, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectTop", nullptr},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_NONE, u"cmd_beginLine", nullptr, nullptr, u"cmd_beginLine", u"cmd_beginLine"},
+ {NS_VK_END, 0, MODIFIER_NONE, u"cmd_endLine", nullptr, nullptr, u"cmd_endLine", u"cmd_endLine"},
+ {NS_VK_HOME, 0, MODIFIER_SHIFT, u"cmd_selectBeginLine", nullptr, nullptr, u"cmd_selectBeginLine", u"cmd_selectBeginLine"},
+ {NS_VK_END, 0, MODIFIER_SHIFT, u"cmd_selectEndLine", nullptr, nullptr, u"cmd_selectEndLine", u"cmd_selectEndLine"},
+ {NS_VK_HOME, 0, MODIFIER_CONTROL, u"cmd_moveTop", nullptr, nullptr, u"cmd_moveTop", u"cmd_moveTop"},
+ {NS_VK_END, 0, MODIFIER_CONTROL, u"cmd_moveBottom", nullptr, nullptr, u"cmd_moveBottom", u"cmd_moveBottom"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectTop", nullptr, nullptr, u"cmd_selectTop", u"cmd_selectTop"},
+ {NS_VK_END, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectBottom", nullptr, nullptr, u"cmd_selectBottom", u"cmd_selectBottom"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_INSERT, 0, MODIFIER_SHIFT, u"cmd_paste", nullptr, nullptr, nullptr, u"cmd_paste"},
+ {NS_VK_INSERT, 0, MODIFIER_CONTROL, u"cmd_copy", nullptr, nullptr, nullptr, u"cmd_copy"},
+ {NS_VK_DELETE, 0, MODIFIER_SHIFT, u"cmd_cutOrDelete", nullptr, nullptr, nullptr, u"cmd_cutOrDelete"},
+ {NS_VK_DELETE, 0, MODIFIER_CONTROL, u"cmd_deleteWordForward", nullptr, nullptr, u"cmd_deleteWordForward", u"cmd_copyOrDelete"},
+ {NS_VK_DELETE, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine", nullptr},
+ {NS_VK_BACK, 0, MODIFIER_CONTROL, u"cmd_deleteWordBackward", nullptr, nullptr, u"cmd_deleteWordBackward", u"cmd_deleteWordBackward"},
+ {NS_VK_BACK, 0, MODIFIER_ALT, u"cmd_undo", nullptr, nullptr, u"cmd_deleteToBeginningOfLine", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_BACK, 0, MODIFIER_SHIFT | MODIFIER_ALT, u"cmd_redo", nullptr, nullptr, nullptr, nullptr},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, 'a', MODIFIER_ACCEL, u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", nullptr},
+ {0, 'a', MODIFIER_ALT, nullptr, nullptr, nullptr, nullptr, u"cmd_selectAll"},
+ {0, 'c', MODIFIER_ACCEL, u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy"},
+ {0, 'x', MODIFIER_ACCEL, u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut"},
+ {0, 'v', MODIFIER_ACCEL, u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste"},
+ {0, 'y', MODIFIER_ACCEL, u"cmd_redo", nullptr, u"cmd_redo", nullptr, u"cmd_redo"},
+ {0, 'z', MODIFIER_ACCEL, u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo"},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste"},
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ALT | MODIFIER_ACCEL, nullptr, u"cmd_paste", nullptr, nullptr, nullptr},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'z', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo"},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'a', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_beginLine"},
+ {0, 'b', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_charPrevious"},
+ {0, 'd', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteCharForward"},
+ {0, 'e', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_endLine"},
+ {0, 'f', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_charNext"},
+ {0, 'h', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteCharBackward"},
+ {0, 'k', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine"},
+ {0, 'n', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_lineNext"},
+ {0, 'p', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_linePrevious"},
+ {0, 'u', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteToBeginningOfLine"},
+ {0, 'w', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteWordBackward"},
+ };
+ // clang-format on
+
+ TestAllKeyCodes(ShortcutKeys::GetHandlers(HandlerType::eTextArea),
+ expectedMap);
+
+ ShortcutKeys::Shutdown(); // Destroy the singleton instance.
+}
+
+TEST(ShortcutKeyDefinitions, Browser)
+{
+ // clang-format off
+ const nsTArray<ShortcutKeyMap> expectedMap{
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_NONE, u"cmd_moveLeft", u"cmd_moveLeft", u"cmd_moveLeft", u"cmd_moveLeft", u"cmd_moveLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_NONE, u"cmd_moveRight", u"cmd_moveRight", u"cmd_moveRight", u"cmd_moveRight", u"cmd_moveRight"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT, u"cmd_selectLeft", u"cmd_selectLeft", u"cmd_selectLeft", u"cmd_selectCharPrevious", u"cmd_selectLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT, u"cmd_selectRight", u"cmd_selectRight", u"cmd_selectRight", u"cmd_selectCharNext", u"cmd_selectRight"},
+ {NS_VK_LEFT, 0, MODIFIER_CONTROL, u"cmd_moveLeft2", nullptr, u"cmd_moveLeft2", u"cmd_wordPrevious", u"cmd_wordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_CONTROL, u"cmd_moveRight2", nullptr, u"cmd_moveRight2", u"cmd_wordNext", u"cmd_wordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_ALT, nullptr, u"cmd_moveLeft2", nullptr, u"cmd_beginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_ALT, nullptr, u"cmd_moveRight2", nullptr, u"cmd_endLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_NONE, u"cmd_moveUp", u"cmd_moveUp", u"cmd_moveUp", u"cmd_moveUp", u"cmd_moveUp"},
+ {NS_VK_DOWN, 0, MODIFIER_NONE, u"cmd_moveDown", u"cmd_moveDown", u"cmd_moveDown", u"cmd_moveDown", u"cmd_moveDown"},
+ {NS_VK_UP, 0, MODIFIER_SHIFT, u"cmd_selectUp", u"cmd_selectUp", u"cmd_selectUp", u"cmd_selectLinePrevious", u"cmd_selectUp"},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectDown", u"cmd_selectDown", u"cmd_selectDown", u"cmd_selectLineNext", u"cmd_selectDown"},
+ {NS_VK_UP, 0, MODIFIER_CONTROL, u"cmd_moveUp2", nullptr, u"cmd_moveUp2", nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_CONTROL, u"cmd_moveDown2", nullptr, u"cmd_moveDown2", nullptr, nullptr},
+ {NS_VK_UP, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveTop", nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveBottom", nullptr},
+ {NS_VK_UP, 0, MODIFIER_META, nullptr, u"cmd_moveUp2", nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_META, nullptr, u"cmd_moveDown2", nullptr, nullptr, nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectLeft2", nullptr, u"cmd_selectLeft2", u"cmd_selectWordPrevious", u"cmd_selectWordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectRight2", nullptr, u"cmd_selectRight2", u"cmd_selectWordNext", u"cmd_selectWordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, u"cmd_selectLeft2", nullptr, u"cmd_selectBeginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, u"cmd_selectRight2", nullptr, u"cmd_selectEndLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectUp2", nullptr, u"cmd_selectUp2", nullptr, u"cmd_selectWordPrevious"},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectDown2", nullptr, u"cmd_selectDown2", nullptr, u"cmd_selectWordNext"},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, u"cmd_selectUp2", nullptr, u"cmd_selectTop", nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, u"cmd_selectDown2", nullptr, u"cmd_selectBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_PAGE_UP, 0, MODIFIER_NONE, u"cmd_movePageUp", u"cmd_scrollPageUp", u"cmd_movePageUp", u"cmd_movePageUp", u"cmd_movePageUp"},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_NONE, u"cmd_movePageDown", u"cmd_scrollPageDown", u"cmd_movePageDown", u"cmd_movePageDown", u"cmd_movePageDown"},
+ {NS_VK_PAGE_UP, 0, MODIFIER_SHIFT, u"cmd_selectPageUp", nullptr, u"cmd_selectPageUp", u"cmd_selectPageUp", u"cmd_selectPageUp"},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectPageDown", nullptr, u"cmd_selectPageDown", u"cmd_selectPageDown", u"cmd_selectPageDown"},
+ {NS_VK_PAGE_UP, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveTop", nullptr},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_PAGE_UP, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectTop", nullptr},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_NONE, u"cmd_beginLine", u"cmd_scrollTop", u"cmd_beginLine", u"cmd_beginLine", u"cmd_beginLine"},
+ {NS_VK_END, 0, MODIFIER_NONE, u"cmd_endLine", u"cmd_scrollBottom", u"cmd_endLine", u"cmd_endLine", u"cmd_endLine"},
+ {NS_VK_HOME, 0, MODIFIER_SHIFT, u"cmd_selectBeginLine", nullptr, u"cmd_selectBeginLine", u"cmd_selectBeginLine", u"cmd_selectBeginLine"},
+ {NS_VK_END, 0, MODIFIER_SHIFT, u"cmd_selectEndLine", nullptr, u"cmd_selectEndLine", u"cmd_selectEndLine", u"cmd_selectEndLine"},
+ {NS_VK_HOME, 0, MODIFIER_CONTROL, u"cmd_moveTop", nullptr, u"cmd_moveTop", u"cmd_moveTop", u"cmd_moveTop"},
+ {NS_VK_END, 0, MODIFIER_CONTROL, u"cmd_moveBottom", nullptr, u"cmd_moveBottom", u"cmd_moveBottom", u"cmd_moveBottom"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectTop", nullptr, u"cmd_selectTop", u"cmd_selectTop", u"cmd_selectTop"},
+ {NS_VK_END, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectBottom", nullptr, u"cmd_selectBottom", u"cmd_selectBottom", u"cmd_selectBottom"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_INSERT, 0, MODIFIER_CONTROL, u"cmd_copy", nullptr, u"cmd_copy", nullptr, u"cmd_copy"},
+ {NS_VK_DELETE, 0, MODIFIER_SHIFT, u"cmd_cut", nullptr, u"cmd_cut", nullptr, u"cmd_cut"},
+ {NS_VK_DELETE, 0, MODIFIER_CONTROL, u"cmd_deleteWordForward", nullptr, u"cmd_copy", u"cmd_deleteWordForward", u"cmd_copy"},
+ {NS_VK_DELETE, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine", nullptr},
+ {NS_VK_BACK, 0, MODIFIER_CONTROL, nullptr, nullptr, nullptr, u"cmd_deleteWordBackward", nullptr},
+ {NS_VK_BACK, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_deleteToBeginningOfLine", nullptr},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, ' ', MODIFIER_NONE, u"cmd_scrollPageDown", u"cmd_scrollPageDown", u"cmd_scrollPageDown", u"cmd_scrollPageDown", u"cmd_scrollPageDown"},
+ {0, ' ', MODIFIER_SHIFT, u"cmd_scrollPageUp", u"cmd_scrollPageUp", u"cmd_scrollPageUp", u"cmd_scrollPageUp", u"cmd_scrollPageUp"},
+ {0, 'a', MODIFIER_ACCEL, u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll"},
+ {0, 'a', MODIFIER_ALT, nullptr, nullptr, nullptr, nullptr, u"cmd_selectAll"},
+ {0, 'c', MODIFIER_ACCEL, u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy"},
+ {0, 'x', MODIFIER_ACCEL, u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut"},
+ {0, 'v', MODIFIER_ACCEL, u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste"},
+ {0, 'y', MODIFIER_ACCEL, u"cmd_redo", nullptr, nullptr, nullptr, nullptr},
+ {0, 'z', MODIFIER_ACCEL, u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo"},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting"},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ALT | MODIFIER_ACCEL, nullptr, u"cmd_pasteNoFormatting", nullptr, nullptr, nullptr},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'z', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo"},
+ };
+ // clang-format on
+
+ TestAllKeyCodes(ShortcutKeys::GetHandlers(HandlerType::eBrowser),
+ expectedMap);
+
+ ShortcutKeys::Shutdown(); // Destroy the singleton instance.
+}
+
+TEST(ShortcutKeyDefinitions, HTMLEditor)
+{
+ // clang-format off
+ const nsTArray<ShortcutKeyMap> expectedMap{
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_NONE, u"cmd_moveLeft", nullptr, u"cmd_moveLeft", u"cmd_moveLeft", u"cmd_moveLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_NONE, u"cmd_moveRight", nullptr, u"cmd_moveRight", u"cmd_moveRight", u"cmd_moveRight"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT, u"cmd_selectLeft", nullptr, u"cmd_selectLeft", u"cmd_selectLeft", u"cmd_selectLeft"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT, u"cmd_selectRight", nullptr, u"cmd_selectRight", u"cmd_selectRight", u"cmd_selectRight"},
+ {NS_VK_LEFT, 0, MODIFIER_CONTROL, u"cmd_moveLeft2", nullptr, nullptr, u"cmd_wordPrevious", u"cmd_wordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_CONTROL, u"cmd_moveRight2", nullptr, nullptr, u"cmd_wordNext", u"cmd_wordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_beginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_endLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_NONE, u"cmd_moveUp", nullptr, u"cmd_moveUp", u"cmd_moveUp", u"cmd_moveUp"},
+ {NS_VK_DOWN, 0, MODIFIER_NONE, u"cmd_moveDown", nullptr, u"cmd_moveDown", u"cmd_moveDown", u"cmd_moveDown"},
+ {NS_VK_UP, 0, MODIFIER_SHIFT, u"cmd_selectUp", nullptr, u"cmd_selectUp", u"cmd_selectUp", u"cmd_selectUp"},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectDown", nullptr, u"cmd_selectDown", u"cmd_selectDown", u"cmd_selectDown"},
+ {NS_VK_UP, 0, MODIFIER_CONTROL, u"cmd_moveUp2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_CONTROL, u"cmd_moveDown2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_UP, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveTop", nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectLeft2", nullptr, nullptr, u"cmd_selectWordPrevious", u"cmd_selectWordPrevious"},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectRight2", nullptr, nullptr, u"cmd_selectWordNext", u"cmd_selectWordNext"},
+ {NS_VK_LEFT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBeginLine", nullptr},
+ {NS_VK_RIGHT, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectEndLine", nullptr},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectUp2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectDown2", nullptr, nullptr, nullptr, nullptr},
+ {NS_VK_UP, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectTop", nullptr},
+ {NS_VK_DOWN, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_PAGE_UP, 0, MODIFIER_NONE, u"cmd_movePageUp", nullptr, nullptr, u"cmd_movePageUp", u"cmd_movePageUp"},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_NONE, u"cmd_movePageDown", nullptr, nullptr, u"cmd_movePageDown", u"cmd_movePageDown"},
+ {NS_VK_PAGE_UP, 0, MODIFIER_SHIFT, u"cmd_selectPageUp", nullptr, nullptr, u"cmd_selectPageUp", u"cmd_selectPageUp"},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_SHIFT, u"cmd_selectPageDown", nullptr, nullptr, u"cmd_selectPageDown", u"cmd_selectPageDown"},
+ {NS_VK_PAGE_UP, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveTop", nullptr},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_moveBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_PAGE_UP, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectTop", nullptr},
+ {NS_VK_PAGE_DOWN, 0, MODIFIER_SHIFT | MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_selectBottom", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_NONE, u"cmd_beginLine", nullptr, nullptr, u"cmd_beginLine", u"cmd_beginLine"},
+ {NS_VK_END, 0, MODIFIER_NONE, u"cmd_endLine", nullptr, nullptr, u"cmd_endLine", u"cmd_endLine"},
+ {NS_VK_HOME, 0, MODIFIER_SHIFT, u"cmd_selectBeginLine", nullptr, nullptr, u"cmd_selectBeginLine", u"cmd_selectBeginLine"},
+ {NS_VK_END, 0, MODIFIER_SHIFT, u"cmd_selectEndLine", nullptr, nullptr, u"cmd_selectEndLine", u"cmd_selectEndLine"},
+ {NS_VK_HOME, 0, MODIFIER_CONTROL, u"cmd_moveTop", nullptr, nullptr, u"cmd_moveTop", u"cmd_moveTop"},
+ {NS_VK_END, 0, MODIFIER_CONTROL, u"cmd_moveBottom", nullptr, nullptr, u"cmd_moveBottom", u"cmd_moveBottom"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_HOME, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectTop", nullptr, nullptr, u"cmd_selectTop", u"cmd_selectTop"},
+ {NS_VK_END, 0, MODIFIER_SHIFT | MODIFIER_CONTROL, u"cmd_selectBottom", nullptr, nullptr, u"cmd_selectBottom", u"cmd_selectBottom"},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_INSERT, 0, MODIFIER_SHIFT, u"cmd_paste", nullptr, nullptr, nullptr, u"cmd_paste"},
+ {NS_VK_INSERT, 0, MODIFIER_CONTROL, u"cmd_copy", nullptr, nullptr, nullptr, u"cmd_copy"},
+ {NS_VK_DELETE, 0, MODIFIER_SHIFT, u"cmd_cutOrDelete", nullptr, nullptr, nullptr, u"cmd_cutOrDelete"},
+ {NS_VK_DELETE, 0, MODIFIER_CONTROL, u"cmd_deleteWordForward", nullptr, nullptr, u"cmd_deleteWordForward", u"cmd_copyOrDelete"},
+ {NS_VK_DELETE, 0, MODIFIER_ALT, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine", nullptr},
+ {NS_VK_BACK, 0, MODIFIER_CONTROL, u"cmd_deleteWordBackward", nullptr, nullptr, u"cmd_deleteWordBackward", u"cmd_deleteWordBackward"},
+ {NS_VK_BACK, 0, MODIFIER_ALT, u"cmd_undo", nullptr, nullptr, u"cmd_deleteToBeginningOfLine", nullptr},
+
+ // KeyCode Modifiers Windows macOS Linux Android Emacs
+ {NS_VK_BACK, 0, MODIFIER_SHIFT | MODIFIER_ALT, u"cmd_redo", nullptr, nullptr, nullptr, nullptr},
+
+ // charCode Modifiers, Windows macOS Linux Android Emacs
+ {0, ' ', MODIFIER_NONE, u"cmd_scrollPageDown", u"cmd_scrollPageDown", u"cmd_scrollPageDown", u"cmd_scrollPageDown", u"cmd_scrollPageDown"},
+ {0, ' ', MODIFIER_SHIFT, u"cmd_scrollPageUp", u"cmd_scrollPageUp", u"cmd_scrollPageUp", u"cmd_scrollPageUp", u"cmd_scrollPageUp"},
+ {0, 'a', MODIFIER_ACCEL, u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", u"cmd_selectAll", nullptr},
+ {0, 'a', MODIFIER_ALT, nullptr, nullptr, nullptr, nullptr, u"cmd_selectAll"},
+ {0, 'c', MODIFIER_ACCEL, u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy", u"cmd_copy"},
+ {0, 'x', MODIFIER_ACCEL, u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut", u"cmd_cut"},
+ {0, 'v', MODIFIER_ACCEL, u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste", u"cmd_paste"},
+ {0, 'y', MODIFIER_ACCEL, u"cmd_redo", nullptr, u"cmd_redo", nullptr, u"cmd_redo"},
+ {0, 'z', MODIFIER_ACCEL, u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo", u"cmd_undo"},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting", u"cmd_pasteNoFormatting"},
+ {0, 'z', MODIFIER_SHIFT | MODIFIER_ACCEL, u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo", u"cmd_redo"},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'v', MODIFIER_SHIFT | MODIFIER_ACCEL | MODIFIER_ALT, nullptr, u"cmd_pasteNoFormatting", nullptr, nullptr, nullptr},
+
+ // charCode Modifiers Windows macOS Linux Android Emacs
+ {0, 'a', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_beginLine"},
+ {0, 'b', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_charPrevious"},
+ {0, 'd', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteCharForward"},
+ {0, 'e', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_endLine"},
+ {0, 'f', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_charNext"},
+ {0, 'h', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteCharBackward"},
+ {0, 'k', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteToEndOfLine"},
+ {0, 'n', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_lineNext"},
+ {0, 'p', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_linePrevious"},
+ {0, 'u', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteToBeginningOfLine"},
+ {0, 'w', MODIFIER_CONTROL, nullptr, nullptr, nullptr, nullptr, u"cmd_deleteWordBackward"},
+ };
+ // clang-format on
+
+ TestAllKeyCodes(ShortcutKeys::GetHandlers(HandlerType::eEditor), expectedMap);
+
+ ShortcutKeys::Shutdown(); // Destroy the singleton instance.
+}
+
+#undef MODIFIER_ACCEL
+#undef GetCommandForPlatform
+
+} // namespace mozilla
diff --git a/dom/events/test/gtest/moz.build b/dom/events/test/gtest/moz.build
new file mode 100644
index 0000000000..d5c0ac6a9e
--- /dev/null
+++ b/dom/events/test/gtest/moz.build
@@ -0,0 +1,18 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "DOM: UI Events & Focus Handling")
+
+UNIFIED_SOURCES += [
+ "TestShortcutKeyDefinitions.cpp",
+]
+
+LOCAL_INCLUDES += ["/dom/events"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/dom/events/test/mochitest.toml b/dom/events/test/mochitest.toml
new file mode 100644
index 0000000000..b6b9e58368
--- /dev/null
+++ b/dom/events/test/mochitest.toml
@@ -0,0 +1,523 @@
+[DEFAULT]
+# Skip migration work in BG__migrateUI for browser_startup.js since it increases
+# the occurrence of the leak reported in bug 1398563 with test_bug1327798.html.
+# Run the font-loader eagerly to minimize the risk that font list finalization
+# may disrupt the events received or result in a timeout.
+tags = "condprof"
+prefs = [
+ "browser.migration.version=9999999",
+ "gfx.font_loader.delay=0",
+ "ui.dragThresholdX=4", # Bug 1873142
+ "ui.dragThresholdY=4", # Bug 1873142
+]
+support-files = [
+ "bug226361_iframe.xhtml",
+ "bug299673.js",
+ "bug322588-popup.html",
+ "bug426082.html",
+ "bug545268.html",
+ "bug574663.html",
+ "bug607464.html",
+ "bug656379-1.html",
+ "bug418986-3.js",
+ "error_event_worker.js",
+ "empty.js",
+ "event_leak_utils.js",
+ "window_bug493251.html",
+ "window_bug659071.html",
+ "window_wheel_default_action.html",
+ "!/gfx/layers/apz/test/mochitest/apz_test_utils.js",
+]
+
+["test_accel_virtual_modifier.html"]
+
+["test_accesskey.html"]
+
+["test_addEventListenerExtraArg.html"]
+
+["test_all_synthetic_events.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_auxclick_autoscroll_off.html"]
+
+["test_bug226361.xhtml"]
+
+["test_bug238987.html"]
+
+["test_bug288392.html"]
+
+["test_bug299673-1.html"]
+
+["test_bug299673-2.html"]
+
+["test_bug322588.html"]
+
+["test_bug328885.html"]
+
+["test_bug336682_1.html"]
+support-files = ["test_bug336682.js"]
+
+["test_bug367781.html"]
+
+["test_bug379120.html"]
+
+["test_bug402089.html"]
+
+["test_bug405632.html"]
+
+["test_bug409604.html"]
+skip-if = ["os == 'android'"] # android: TIMED_OUT
+
+["test_bug412567.html"]
+
+["test_bug418986-3.html"]
+
+["test_bug422132.html"]
+
+["test_bug426082.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug427537.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug428988.html"]
+
+["test_bug432698.html"]
+
+["test_bug443985.html"]
+skip-if = ["verify"]
+
+["test_bug447736.html"]
+
+["test_bug448602.html"]
+
+["test_bug450876.html"]
+
+["test_bug456273.html"]
+
+["test_bug457672.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug489671.html"]
+
+["test_bug493251.html"]
+
+["test_bug508479.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM # drag event fails
+
+["test_bug517851.html"]
+
+["test_bug534833.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug545268.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug547996-1.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug547996-2.xhtml"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug556493.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug563329.html"]
+skip-if = ["true"] # Disabled due to timeouts.
+
+["test_bug574663.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug591815.html"]
+
+["test_bug593959.html"]
+
+["test_bug603008.html"]
+skip-if = ["os == 'android'"]
+
+["test_bug605242.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug607464.html"]
+skip-if = [
+ "os == 'android'",
+ "e10s", #CRASH_DUMP, RANDOM, bug 1400586
+]
+
+["test_bug613634.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug615597.html"]
+skip-if = ["os == 'android'"] # failed
+
+["test_bug624127.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug635465.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug641477.html"]
+
+["test_bug648573.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug650493.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug656379-1.html"]
+skip-if = ["os == 'android'"]
+
+["test_bug656379-2.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug656954.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug659071.html"]
+skip-if = [
+ "os == 'android'", # fail
+ "http3",
+ "http2",
+]
+
+["test_bug659350.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug662678.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug667612.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug667919-1.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug684208.html"]
+
+["test_bug687787.html"]
+
+["test_bug689564.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug698929.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_bug704423.html"]
+
+["test_bug741666.html"]
+skip-if = ["os == 'android'"] # fail
+
+["test_bug812744.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug822898.html"]
+
+["test_bug855741.html"]
+
+["test_bug864040.html"]
+
+["test_bug924087.html"]
+
+["test_bug930374-content.html"]
+
+["test_bug944011.html"]
+
+["test_bug944847.html"]
+
+["test_bug946632.html"]
+skip-if = ["os == 'android'"] # fail
+
+["test_bug967796.html"]
+
+["test_bug985988.html"]
+
+["test_bug998809.html"]
+
+["test_bug1003432.html"]
+support-files = ["test_bug1003432.js"]
+
+["test_bug1013412.html"]
+
+["test_bug1017086_enable.html"]
+support-files = ["bug1017086_inner.html"]
+
+["test_bug1037990.html"]
+
+["test_bug1079236.html"]
+
+["test_bug1127588.html"]
+
+["test_bug1145910.html"]
+
+["test_bug1150308.html"]
+
+["test_bug1248459.html"]
+
+["test_bug1264380.html"]
+skip-if = ["os == 'android'"] # some clipboard types and drag aren't supported
+
+["test_bug1298970.html"]
+
+["test_bug1304044.html"]
+
+["test_bug1305458.html"]
+
+["test_bug1327798.html"]
+skip-if = ["headless"]
+
+["test_bug1332699.html"]
+
+["test_bug1339758.html"]
+
+["test_bug1369072.html"]
+support-files = ["window_bug1369072.html"]
+skip-if = ["os == 'android'"]
+
+["test_bug1429572.html"]
+support-files = ["window_bug1429572.html"]
+skip-if = ["os == 'android'"] # failed
+
+["test_bug1446834.html"]
+support-files = ["file_bug1446834.html"]
+
+["test_bug1447993.html"]
+support-files = ["window_bug1447993.html"]
+skip-if = ["os == 'android'"]
+
+["test_bug1484371.html"]
+support-files = ["file_bug1484371.html"]
+
+["test_bug1534562.html"]
+skip-if = ["os == 'android'"] # Bug 1312791
+
+["test_bug1539497.html"]
+
+["test_bug1581192.html"]
+
+["test_bug1635018.html"]
+
+["test_bug1637259.html"]
+disabled = "Enable this when the taskcluster Windows machine upgrades to RS5+"
+run-if = ["os == 'win'"] # Only Windows supports pen input synthesis
+support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"]
+
+["test_bug1673434.html"]
+
+["test_bug1681800.html"]
+
+["test_bug1686716.html"]
+
+["test_bug1692052.html"]
+support-files = ["file_bug1692052.html"]
+
+["test_bug1692277.html"]
+disabled = "Enable this when the taskcluster Windows machine upgrades to RS5+"
+run-if = ["os == 'win'"] # Only Windows supports pen input synthesis
+support-files = [
+ "!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+ "!/dom/base/test/Ahem.ttf",
+]
+
+["test_bug1709832.html"]
+support-files = ["!/dom/base/test/Ahem.ttf"]
+
+["test_bug1710509.html"]
+disabled = "Enable this when the taskcluster Windows machine upgrades to RS5+"
+run-if = ["os == 'win'"] # Only Windows supports pen input synthesis
+support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"]
+
+["test_bug1728171.html"]
+run-if = ["os == 'win'"] # Only Windows 1809+ supports pen input synthesis
+support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"]
+
+["test_click_on_reframed_generated_text.html"]
+
+["test_click_on_restyled_element.html"]
+
+["test_clickevent_on_input.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_coalesce_mousewheel.html"]
+skip-if = ["os == 'android'"] # fail
+
+["test_coalesce_touchmove.html"]
+support-files = [
+ "file_coalesce_touchmove_ipc.html",
+ "file_coalesce_touchmove_browserchild.html",
+ "file_coalesce_touchmove_browserchild2.html",
+]
+skip-if = ["debug"] #In order to be able to test touchmoves, the test needs to synthesize touchstart in a way which asserts
+
+["test_continuous_wheel_events.html"]
+skip-if = [
+ "verify && debug && (os == 'linux' || os == 'win')",
+ "os == 'android'", # wheel event isn't supported
+]
+
+["test_dblclick_explicit_original_target.html"]
+
+["test_deltaMode_lines_always_enabled.html"]
+
+["test_deviceSensor.html"]
+
+["test_disabled_events.html"]
+
+["test_dnd_with_modifiers.html"]
+
+["test_dom_activate_event.html"]
+
+["test_dom_keyboard_event.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_dom_mouse_event.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_dom_storage_event.html"]
+
+["test_dom_wheel_event.html"]
+skip-if = ["os == 'android'"] # wheel scroll isn't supported
+
+["test_drag_coords.html"]
+skip-if = ["os == 'android'"] # Need calculate screen coordinates.
+
+["test_drag_image_file.html"]
+skip-if = [
+ "xorigin", # Bug 1802904
+]
+support-files = ["green.png"]
+
+["test_draggableprop.html"]
+
+["test_dragstart.html"]
+
+["test_error_events.html"]
+skip-if = ["os == 'android'"] #TIMED_OUT
+
+["test_eventTimeStamp.html"]
+
+["test_event_handler_cc.html"]
+
+["test_event_screenXY_in_cross_origin_iframe.html"]
+support-files = [
+ "file_event_screenXY.html",
+ "!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+]
+skip-if = ["os == 'android'"] # fail
+
+["test_event_screenXY_with_zoom.html"]
+skip-if = ["os == 'android'"] # Android doesn't have full zoom.
+
+["test_eventctors.html"]
+skip-if = ["os == 'android'"] #CRASH_DUMP, RANDOM
+
+["test_eventctors_sensors.html"]
+
+["test_eventhandler_scoping.html"]
+
+["test_focus_abspos.html"]
+
+["test_focus_blur_on_click_in_cross_origin_iframe.html"]
+support-files = ["file_focus_blur_on_click_in_cross_origin_iframe.html"]
+skip-if = ["os == 'android'"] # Bug 1701546
+
+["test_focus_blur_on_click_in_deep_cross_origin_iframe.html"]
+support-files = [
+ "file_focus_blur_on_click_in_deep_cross_origin_iframe_inner.html",
+ "file_focus_blur_on_click_in_deep_cross_origin_iframe_middle.html",
+]
+skip-if = [
+ "os == 'android'", # Bug 1701546
+ "condprof && os == 'win'", # 1773806
+]
+
+["test_hover_mouseleave.html"]
+
+["test_legacy_event.html"]
+
+["test_legacy_touch_api.html"]
+
+["test_marquee_events.html"]
+
+["test_messageEvent.html"]
+
+["test_messageEvent_init.html"]
+
+["test_mouse_capture_iframe.html"]
+support-files = ["file_empty.html"]
+skip-if = [
+ "os == 'android'", # timeout
+ "http3",
+ "http2",
+]
+
+["test_mouse_enterleave_iframe.html"]
+support-files = ["file_mouse_enterleave.html"]
+skip-if = [
+ "!debug", # Bug 1781668
+ "os == 'android'", # timeout
+ "display == 'wayland' && os_version == '22.04'", # Bug 1857022
+ "http3",
+ "http2",
+]
+
+["test_mouse_over_at_removing_down_target.html"]
+
+["test_moving_and_expanding_selection_per_page.html"]
+support-files = ["window_empty_document.html"]
+
+["test_moz_mouse_pixel_scroll_event.html"]
+
+["test_offsetxy.html"]
+
+["test_onerror_handler_args.html"]
+
+["test_passive_listeners.html"]
+
+["test_scroll_per_page.html"]
+support-files = ["window_empty_document.html"]
+skip-if = ["os == 'android'"] # fail
+
+["test_selection_after_right_click.html"]
+
+["test_slotted_mouse_event.html"]
+skip-if = ["os == 'android'"] # timeout
+
+["test_slotted_text_click.html"]
+
+["test_submitevent_on_form.html"]
+
+["test_text_event_in_content.html"]
+
+["test_unbound_before_in_active_chain.html"]
+
+["test_use_conflated_keypress_event_model_on_newer_Office_Online_Server.html"]
+
+["test_use_split_keypress_event_model_on_old_Confluence.html"]
+skip-if = ["!debug"] # The mode change event is available only on debug build
+
+["test_use_split_keypress_event_model_on_old_Office_Online_Server.html"]
+skip-if = ["!debug"] # The mode change event is available only on debug build
+
+["test_wheel_default_action.html"]
+skip-if = [
+ "os == 'linux'",
+ "os == 'android'",
+]
+
+["test_wheel_zoom_on_form_controls.html"]
+skip-if = [
+ "verify",
+ "os == 'android'", # wheel isn't supported
+]
diff --git a/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html
new file mode 100644
index 0000000000..55c4e3cad5
--- /dev/null
+++ b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_1.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events properties tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="wpt/pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <!--script src="/resources/testharnessreport.js"></script-->
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="wpt/pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("implicit pointer capture for touch");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ let target0 = window.document.getElementById("target0");
+ let target1 = window.document.getElementById("target1");
+
+ on_event(target0, "pointerdown", function (event) {
+ pointerdown_event = event;
+ detected_pointertypes[event.pointerType] = true;
+ assert_true(true, "target0 receives pointerdown");
+ });
+
+ on_event(target0, "pointermove", function (event) {
+ assert_true(true, "target0 receives pointermove");
+ assert_true(target0.hasPointerCapture(event.pointerId), "target0.hasPointerCapture should be true");
+ });
+
+ on_event(target0, "gotpointercapture", function (event) {
+ assert_true(true, "target0 should receive gotpointercapture");
+ });
+
+ on_event(target0, "lostpointercapture", function (event) {
+ assert_true(true, "target0 should receive lostpointercapture");
+ });
+
+ on_event(target1, "pointermove", function (event) {
+ assert_true(false, "target1 should not receive pointermove");
+ });
+
+ on_event(target0, "pointerup", function (event) {
+ assert_true(true, "target0 receives pointerup");
+ test_pointerEvent.done();
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events tests</h1>
+ <div id="target0" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+ <div id="target1" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html
new file mode 100644
index 0000000000..a533429acb
--- /dev/null
+++ b/dom/events/test/pointerevents/bug1293174_implicit_pointer_capture_for_touch_2.html
@@ -0,0 +1,64 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events properties tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="wpt/pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <!--script src="/resources/testharnessreport.js"></script-->
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="wpt/pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("implicit pointer capture for touch");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ let target0 = window.document.getElementById("target0");
+ let target1 = window.document.getElementById("target1");
+
+ on_event(target0, "pointerdown", function (event) {
+ pointerdown_event = event;
+ detected_pointertypes[event.pointerType] = true;
+ assert_true(true, "target0 receives pointerdown");
+ });
+
+ on_event(target0, "pointermove", function (event) {
+ assert_true(true, "target0 receives pointermove");
+ assert_false(target0.hasPointerCapture(event.pointerId), "target0.hasPointerCapture should be false");
+ });
+
+ on_event(target0, "gotpointercapture", function (event) {
+ assert_unreached("target0 should not receive gotpointercapture");
+ });
+
+ on_event(target0, "lostpointercapture", function (event) {
+ assert_unreached("target0 should not receive lostpointercapture");
+ });
+
+ on_event(target1, "pointermove", function (event) {
+ assert_true(true, "target1 receives pointermove");
+ assert_false(target1.hasPointerCapture(event.pointerId), "target1.hasPointerCapture should be false");
+ });
+
+ on_event(target0, "pointerup", function (event) {
+ assert_true(true, "target0 receives pointerup");
+ test_pointerEvent.done();
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events tests</h1>
+ <div id="target0" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+ <div id="target1" style="width: 200px; height: 200px; background: green" touch-action:none></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/bug968148_inner.html b/dom/events/test/pointerevents/bug968148_inner.html
new file mode 100644
index 0000000000..464e2e5c38
--- /dev/null
+++ b/dom/events/test/pointerevents/bug968148_inner.html
@@ -0,0 +1,316 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=968148
+-->
+<head>
+ <title>Test for Bug 968148</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968148">Mozilla Bug 968148</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 968148, test orignally copied from test_bug582771.html.
+ * Mouse functionality converted to pointer and all steps duplicated in order to run them in parallel for two different pointer Id's
+**/
+
+function ok(condition, msg) {
+ parent.ok(condition, msg);
+}
+
+function is(a, b, msg) {
+ parent.is(a, b, msg);
+}
+
+var test1d1;
+var test1d2;
+var test2d1;
+var test2d2;
+var test1d1pointermovecount = 0;
+var test1d2pointermovecount = 0;
+var test2d1pointermovecount = 0;
+var test2d2pointermovecount = 0;
+
+var test1d1pointerlostcapture = 0;
+var test1d2pointerlostcapture = 0;
+var test2d1pointerlostcapture = 0;
+var test2d2pointerlostcapture = 0;
+var test1d1pointergotcapture = 0;
+var test1d2pointergotcapture = 0;
+var test2d1pointergotcapture = 0;
+var test2d2pointergotcapture = 0;
+var test1PointerId = 1;
+var test2PointerId = 2;
+var rx = 1;
+var ry = 1;
+var angle = 0;
+var force = 1;
+var modifiers = 0;
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+function log(s) {
+ document.getElementById("l").textContent += s + "\n";
+}
+
+function test1d1IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test1d1, "check target is 1d1.");
+ ++test1d1pointermovecount;
+}
+
+function test1d2IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test1d2, "check target is 1d2.");
+ ++test1d2pointermovecount;
+}
+
+function test2d1IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test2d1, "check target is 2d1.");
+ ++test2d1pointermovecount;
+}
+
+function test2d2IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test2d2, "check target is 2d2.");
+ ++test2d2pointermovecount;
+}
+
+function test1d1CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test1d1.setPointerCapture(e.pointerId);
+}
+
+function test1d2CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test1d2.setPointerCapture(e.pointerId);
+}
+
+function test2d1CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test2d1.setPointerCapture(e.pointerId);
+}
+
+function test2d2CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test2d2.setPointerCapture(e.pointerId);
+}
+
+function test1d1PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d1pointergotcapture;
+}
+
+function test1d1PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d1pointerlostcapture;
+}
+
+function test2d1PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d1pointergotcapture;
+}
+
+function test2d1PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d1pointerlostcapture;
+}
+
+function test1d2PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d2pointergotcapture;
+}
+
+function test1d2PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d2pointerlostcapture;
+}
+
+function test2d2PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d2pointergotcapture;
+}
+
+function test2d2PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d2pointerlostcapture;
+}
+
+function runTests() {
+ test1d1 = document.getElementById("test1d1");
+ test1d2 = document.getElementById("test1d2");
+ test2d1 = document.getElementById("test2d1");
+ test2d2 = document.getElementById("test2d2");
+
+ var rect1d1 = test1d1.getBoundingClientRect();
+ var rect1d2 = test1d2.getBoundingClientRect();
+ var rect2d1 = test2d1.getBoundingClientRect();
+ var rect2d2 = test2d2.getBoundingClientRect();
+
+ var left1d1 = rect1d1.left + 5;
+ var top1d1 = rect1d1.top + 5;
+ var left1d2 = rect1d2.left + 5;
+ var top1d2 = rect1d2.top + 5;
+ var left2d1 = rect2d1.left + 5;
+ var top2d1 = rect2d1.top + 5;
+ var left2d2 = rect2d2.left + 5;
+ var top2d2 = rect2d2.top + 5;
+
+ test1d1.addEventListener("pointermove", test1d1IncreasePointerMoveCount, true);
+ test1d2.addEventListener("pointermove", test1d2IncreasePointerMoveCount, true);
+ test2d1.addEventListener("pointermove", test2d1IncreasePointerMoveCount, true);
+ test2d2.addEventListener("pointermove", test2d2IncreasePointerMoveCount, true);
+
+ test1d1.addEventListener("gotpointercapture", test1d1PointerGotCapture, true);
+ test1d1.addEventListener("lostpointercapture", test1d1PointerLostCapture, true);
+
+ test2d1.addEventListener("gotpointercapture", test2d1PointerGotCapture, true);
+ test2d1.addEventListener("lostpointercapture", test2d1PointerLostCapture, true);
+
+ test1d2.addEventListener("gotpointercapture", test1d2PointerGotCapture, true);
+ test1d2.addEventListener("lostpointercapture", test1d2PointerLostCapture, true);
+
+ test2d2.addEventListener("gotpointercapture", test2d2PointerGotCapture, true);
+ test2d2.addEventListener("lostpointercapture", test2d2PointerLostCapture, true);
+
+ document.body.offsetLeft;
+
+ // This shouldn't enable capturing, since we're not in a right kind of
+ // event listener.
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d2, left2d2], [top1d2, top2d2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // No implicitly / explicitly pointer capture. pointermove should be
+ // dispatched to 1d2, 2d2
+ is(test1d1pointermovecount, 0, "1d1 shouldn't have got pointermove");
+ is(test2d1pointermovecount, 0, "2d1 shouldn't have got pointermove");
+ is(test1d2pointermovecount, 1, "1d2 should have got pointermove");
+ is(test2d2pointermovecount, 1, "2d2 should have got pointermove");
+
+ // Explicitly capture pointer to 1d1, 2d1
+ test1d1.addEventListener("pointerdown", test1d1CapturePointer, {capture: true, once: true});
+ test2d1.addEventListener("pointerdown", test2d1CapturePointer, {capture: true, once: true});
+
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d2, left2d2], [top1d2, top2d2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Explicitly capture pointer to 1d1, 2d1. pointermove, gotpointercapture
+ // should be dispatched to 1d1, 2d1
+ is(test1d1pointermovecount, 1, "1d1 should have got pointermove");
+ is(test1d1pointergotcapture, 1, "1d1 should have got pointergotcapture");
+ is(test1d2pointermovecount, 1, "1d2 shouldn't have got pointermove");
+ is(test1d2pointergotcapture, 0, "1d2 shouldn't have got pointergotcapture");
+
+ is(test2d1pointermovecount, 1, "2d1 should have got pointermove");
+ is(test2d1pointergotcapture, 1, "2d1 should have got pointergotcapture");
+ is(test2d2pointermovecount, 1, "2d2 shouldn't have got pointermove");
+ is(test2d2pointergotcapture, 0, "2d2 shouldn't have got pointergotcapture");
+
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Explicitly capture pointer to 1d1, 2d1 when pointerdown
+ test1d1.addEventListener("pointerdown", test1d1CapturePointer, {capture: true, once: true});
+ test2d1.addEventListener("pointerdown", test2d1CapturePointer, {capture: true, once: true});
+
+ // Explicitly capture pointer to 1d2, 2d2 when pointermove
+ test1d1.addEventListener("pointermove", test1d2CapturePointer, {capture: true, once: true});
+ test2d1.addEventListener("pointermove", test2d2CapturePointer, {capture: true, once: true});
+
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // This should send pointer event to test1d1, test2d1.
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d1 + 5, left2d1 + 5], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // This should send pointer event to test1d2, test2d2.
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d1 + 5, left2d1 + 5], [top1d1 + 5, top2d1 + 5], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ is(test1d1pointermovecount, 2, "1d1 should have got pointermove");
+ is(test1d1pointergotcapture, 2, "1d1 should have got pointergotcapture");
+ is(test1d2pointermovecount, 2, "1d2 should have got pointermove");
+ is(test1d2pointergotcapture, 1, "1d2 should have got pointergotcapture");
+
+ is(test2d1pointermovecount, 2, "2d1 should have got pointermove");
+ is(test2d1pointergotcapture, 2, "2d1 should have got pointergotcapture");
+ is(test2d2pointermovecount, 2, "2d2 should have got pointermove");
+ is(test2d2pointergotcapture, 1, "2d2 should have got pointergotcapture");
+
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ 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);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<div class="test" id="test1d1">&nbsp;</div><br><div class="test" id="test1d2">&nbsp;</div>
+<div class="test" id="test2d1">&nbsp;</div><br><div class="test" id="test2d2">&nbsp;</div>
+<pre id="l"></pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/bug968148_inner2.html b/dom/events/test/pointerevents/bug968148_inner2.html
new file mode 100644
index 0000000000..ffad5297bc
--- /dev/null
+++ b/dom/events/test/pointerevents/bug968148_inner2.html
@@ -0,0 +1,315 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=968148
+-->
+<head>
+ <title>Test for Bug 968148</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=968148">Mozilla Bug 968148</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 968148, test orignally copied from test_bug582771.html.
+ * Mouse functionality converted to pointer and all steps duplicated in order to run them in parallel for two different pointer Id's
+**/
+
+function ok(condition, msg) {
+ parent.ok(condition, msg);
+}
+
+function is(a, b, msg) {
+ parent.is(a, b, msg);
+}
+
+var test1d1;
+var test1d2;
+var test2d1;
+var test2d2;
+var test1d1pointermovecount = 0;
+var test1d2pointermovecount = 0;
+var test2d1pointermovecount = 0;
+var test2d2pointermovecount = 0;
+
+var test1d1pointerlostcapture = 0;
+var test1d2pointerlostcapture = 0;
+var test2d1pointerlostcapture = 0;
+var test2d2pointerlostcapture = 0;
+var test1d1pointergotcapture = 0;
+var test1d2pointergotcapture = 0;
+var test2d1pointergotcapture = 0;
+var test2d2pointergotcapture = 0;
+var test1PointerId = 1;
+var test2PointerId = 2;
+var rx = 1;
+var ry = 1;
+var angle = 0;
+var force = 1;
+var modifiers = 0;
+var utils = SpecialPowers.getDOMWindowUtils(window);
+
+function log(s) {
+ document.getElementById("l").textContent += s + "\n";
+}
+
+function test1d1IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test1d1, "check target is 1d1.");
+ ++test1d1pointermovecount;
+}
+
+function test1d2IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test1d2, "check target is 1d2.");
+ ++test1d2pointermovecount;
+}
+
+function test2d1IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test2d1, "check target is 2d1.");
+ ++test2d1pointermovecount;
+}
+
+function test2d2IncreasePointerMoveCount(e) {
+ log(e.type + ", " + e.target.id);
+ is(e.target, test2d2, "check target is 2d2.");
+ ++test2d2pointermovecount;
+}
+
+function test1d1CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test1d1.setPointerCapture(e.pointerId);
+}
+
+function test1d2CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test1d2.setPointerCapture(e.pointerId);
+}
+
+function test2d1CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test2d1.setPointerCapture(e.pointerId);
+}
+
+function test2d2CapturePointer(e) {
+ log(e.type + ", " + e.target.id);
+ test2d2.setPointerCapture(e.pointerId);
+}
+
+function test1d1PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d1pointergotcapture;
+}
+
+function test1d1PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d1pointerlostcapture;
+}
+
+function test2d1PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d1pointergotcapture;
+}
+
+function test2d1PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d1pointerlostcapture;
+}
+
+function test1d2PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d2pointergotcapture;
+}
+
+function test1d2PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test1d2pointerlostcapture;
+}
+
+function test2d2PointerGotCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d2pointergotcapture;
+}
+
+function test2d2PointerLostCapture(e) {
+ log(e.type + ", " + e.target.id);
+ ++test2d2pointerlostcapture;
+}
+
+function runTests() {
+ test1d1 = document.getElementById("test1d1");
+ test1d2 = document.getElementById("test1d2");
+ test2d1 = document.getElementById("test2d1");
+ test2d2 = document.getElementById("test2d2");
+
+ var rect1d1 = test1d1.getBoundingClientRect();
+ var rect1d2 = test1d2.getBoundingClientRect();
+ var rect2d1 = test2d1.getBoundingClientRect();
+ var rect2d2 = test2d2.getBoundingClientRect();
+
+ var left1d1 = rect1d1.left + 5;
+ var top1d1 = rect1d1.top + 5;
+ var left1d2 = rect1d2.left + 5;
+ var top1d2 = rect1d2.top + 5;
+ var left2d1 = rect2d1.left + 5;
+ var top2d1 = rect2d1.top + 5;
+ var left2d2 = rect2d2.left + 5;
+ var top2d2 = rect2d2.top + 5;
+
+ test1d1.addEventListener("pointermove", test1d1IncreasePointerMoveCount, true);
+ test1d2.addEventListener("pointermove", test1d2IncreasePointerMoveCount, true);
+ test2d1.addEventListener("pointermove", test2d1IncreasePointerMoveCount, true);
+ test2d2.addEventListener("pointermove", test2d2IncreasePointerMoveCount, true);
+
+ test1d1.addEventListener("gotpointercapture", test1d1PointerGotCapture, true);
+ test1d1.addEventListener("lostpointercapture", test1d1PointerLostCapture, true);
+
+ test2d1.addEventListener("gotpointercapture", test2d1PointerGotCapture, true);
+ test2d1.addEventListener("lostpointercapture", test2d1PointerLostCapture, true);
+
+ test1d2.addEventListener("gotpointercapture", test1d2PointerGotCapture, true);
+ test1d2.addEventListener("lostpointercapture", test1d2PointerLostCapture, true);
+
+ test2d2.addEventListener("gotpointercapture", test2d2PointerGotCapture, true);
+ test2d2.addEventListener("lostpointercapture", test2d2PointerLostCapture, true);
+
+ document.body.offsetLeft;
+
+ // This shouldn't enable capturing, since we're not in a right kind of
+ // event listener.
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d2, left2d2], [top1d2, top2d2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Implicitly pointer capture. pointermove should be dispatched to 1d1, 2d1
+ is(test1d1pointermovecount, 1, "1d1 should have got pointermove");
+ is(test2d1pointermovecount, 1, "2d1 should have got pointermove");
+ is(test1d2pointermovecount, 0, "1d2 shouldn't have got pointermove");
+ is(test2d2pointermovecount, 0, "2d2 shouldn't have got pointermove");
+
+ // Explicitly capture pointer to 1d1, 2d1
+ test1d1.addEventListener("pointerdown", test1d1CapturePointer, {capture: true, once: true});
+ test2d1.addEventListener("pointerdown", test2d1CapturePointer, {capture: true, once: true});
+
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d2, left2d2], [top1d2, top2d2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Explicitly capture pointer to 1d1, 2d1. pointermove, gotpointercapture
+ // should be dispatched to 1d1, 2d1
+ is(test1d1pointermovecount, 2, "1d1 should have got pointermove");
+ is(test1d1pointergotcapture, 2, "1d1 should have got pointergotcapture");
+ is(test1d2pointermovecount, 0, "1d2 shouldn't have got pointermove");
+ is(test1d2pointergotcapture, 0, "1d2 shouldn't have got pointergotcapture");
+
+ is(test2d1pointermovecount, 2, "2d1 should have got pointermove");
+ is(test2d1pointergotcapture, 2, "2d1 should have got pointergotcapture");
+ is(test2d2pointermovecount, 0, "2d2 shouldn't have got pointermove");
+ is(test2d2pointergotcapture, 0, "2d2 shouldn't have got pointergotcapture");
+
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Explicitly capture pointer to 1d1, 2d1 when pointerdown
+ test1d1.addEventListener("pointerdown", test1d1CapturePointer, {capture: true, once: true});
+ test2d1.addEventListener("pointerdown", test2d1CapturePointer, {capture: true, once: true});
+
+ // Explicitly capture pointer to 1d2, 2d2 when pointermove
+ test1d1.addEventListener("pointermove", test1d2CapturePointer, {capture: true, once: true});
+ test2d1.addEventListener("pointermove", test2d2CapturePointer, {capture: true, once: true});
+
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // This should send pointer event to test1d1, test2d1.
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d1 + 5, left2d1 + 5], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // This should send pointer event to test1d2, test2d2.
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1d1 + 5, left2d1 + 5], [top1d1 + 5, top2d1 + 5], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ is(test1d1pointermovecount, 3, "1d1 shouldn't have got pointermove");
+ is(test1d1pointergotcapture, 3, "1d1 should have got pointergotcapture");
+ is(test1d2pointermovecount, 1, "1d2 should have got pointermove");
+ is(test1d2pointergotcapture, 1, "1d2 should have got pointergotcapture");
+
+ is(test2d1pointermovecount, 3, "2d1 shouldn't have got pointermove");
+ is(test2d1pointergotcapture, 3, "2d1 should have got pointergotcapture");
+ is(test2d2pointermovecount, 1, "2d2 should have got pointermove");
+ is(test2d2pointergotcapture, 1, "2d2 should have got pointergotcapture");
+
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1d1, left2d1], [top1d1, top2d1], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ 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);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<div class="test" id="test1d1">&nbsp;</div><br><div class="test" id="test1d2">&nbsp;</div>
+<div class="test" id="test2d1">&nbsp;</div><br><div class="test" id="test2d2">&nbsp;</div>
+<pre id="l"></pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/bug_1420589_iframe1.html b/dom/events/test/pointerevents/bug_1420589_iframe1.html
new file mode 100644
index 0000000000..b18d808b84
--- /dev/null
+++ b/dom/events/test/pointerevents/bug_1420589_iframe1.html
@@ -0,0 +1,17 @@
+<body>
+ <script>
+ let touchEvents = ["touchstart", "touchmove", "touchend"];
+ let pointerEvents = ["pointerdown", "pointermove", "pointerup"];
+
+ touchEvents.forEach((event) => {
+ document.addEventListener(event, (e) => {
+ parent.postMessage("iframe1 " + e.type, "*");
+ }, { once: true });
+ });
+ pointerEvents.forEach((event) => {
+ document.addEventListener(event, (e) => {
+ parent.postMessage("iframe1 " + e.type, "*");
+ }, { once: true });
+ });
+ </script>
+</body>
diff --git a/dom/events/test/pointerevents/bug_1420589_iframe2.html b/dom/events/test/pointerevents/bug_1420589_iframe2.html
new file mode 100644
index 0000000000..75aea1d187
--- /dev/null
+++ b/dom/events/test/pointerevents/bug_1420589_iframe2.html
@@ -0,0 +1,17 @@
+<body>
+ <script>
+ let touchEvents = ["touchstart", "touchmove", "touchend"];
+ let pointerEvents = ["pointerdown", "pointermove", "pointerup"];
+
+ touchEvents.forEach((event) => {
+ document.addEventListener(event, (e) => {
+ parent.postMessage("iframe2 " + e.type, "*");
+ }, { once: true });
+ });
+ pointerEvents.forEach((event) => {
+ document.addEventListener(event, (e) => {
+ parent.postMessage("iframe2 " + e.type, "*");
+ }, { once: true });
+ });
+ </script>
+</body>
diff --git a/dom/events/test/pointerevents/chrome.toml b/dom/events/test/pointerevents/chrome.toml
new file mode 100644
index 0000000000..36e54e824a
--- /dev/null
+++ b/dom/events/test/pointerevents/chrome.toml
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+["test_bug1697769.xhtml"]
diff --git a/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe.html b/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe.html
new file mode 100644
index 0000000000..88690748e5
--- /dev/null
+++ b/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1671849
+-->
+<head>
+<title>Bug 1671849</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="pointerevent_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1671849">Mozilla Bug 1671849</a>
+<div id="target"></div>
+<iframe src="https://example.com/tests/dom/events/test/pointerevents/iframe.html"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1671849
+ */
+add_task(async function test_pointer_capture_xorigin_iframe() {
+ let iframe = document.querySelector("iframe");
+ await SpecialPowers.spawn(iframe.contentWindow, [], () => {
+ let unexpected = function(e) {
+ ok(false, `iframe shoule not get any ${e.type} event`);
+ };
+ content.document.body.addEventListener("pointermove", unexpected);
+ content.document.body.addEventListener("pointerup", unexpected);
+ });
+
+ let target = document.getElementById("target");
+ synthesizeMouse(target, 10, 10, { type: "mousedown" });
+ await waitForEvent(target, "pointerdown", function(e) {
+ target.setPointerCapture(e.pointerId);
+ });
+
+ synthesizeMouse(iframe, 10, 10, { type: "mousemove" });
+ await Promise.all([waitForEvent(target, "gotpointercapture"),
+ waitForEvent(target, "pointermove")]);
+
+ synthesizeMouse(iframe, 10, 10, { type: "mouseup" });
+ await Promise.all([waitForEvent(target, "lostpointercapture"),
+ waitForEvent(target, "pointerup")]);
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_pointerlock.html b/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_pointerlock.html
new file mode 100644
index 0000000000..12174da197
--- /dev/null
+++ b/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_pointerlock.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1671849
+-->
+<head>
+<title>Bug 1671849</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="pointerevent_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1671849">Mozilla Bug 1671849</a>
+<div id="target"></div>
+<iframe src="https://example.com/tests/dom/events/test/pointerevents/iframe.html"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1671849
+ */
+function requestPointerLockOnRemoteTarget(aRemoteTarget, aTagName) {
+ return SpecialPowers.spawn(aRemoteTarget, [aTagName], async (tagName) => {
+ SpecialPowers.wrap(content.document).notifyUserGestureActivation();
+ let target = content.document.querySelector(tagName);
+ target.requestPointerLock();
+ await new Promise((aResolve) => {
+ let eventHandler = function(e) {
+ is(e.type, "pointerlockchange", `Got ${e.type} on iframe`);
+ is(content.document.pointerLockElement, target, `pointer lock element`);
+ content.document.removeEventListener("pointerlockchange", eventHandler);
+ content.document.removeEventListener("pointerlockerror", eventHandler);
+ aResolve();
+ };
+ content.document.addEventListener("pointerlockchange", eventHandler);
+ content.document.addEventListener("pointerlockerror", eventHandler);
+ });
+ });
+}
+
+function exitPointerLockOnRemoteTarget(aRemoteTarget) {
+ return SpecialPowers.spawn(aRemoteTarget, [], async () => {
+ content.document.exitPointerLock();
+ await new Promise((aResolve) => {
+ let eventHandler = function(e) {
+ is(e.type, "pointerlockchange", `Got ${e.type} on iframe`);
+ is(content.document.pointerLockElement, null, `pointer lock element`);
+ content.document.removeEventListener("pointerlockchange", eventHandler);
+ content.document.removeEventListener("pointerlockerror", eventHandler);
+ aResolve();
+ };
+ content.document.addEventListener("pointerlockchange", eventHandler);
+ content.document.addEventListener("pointerlockerror", eventHandler);
+ });
+ });
+}
+
+function waitEventOnRemoteTarget(aRemoteTarget, aEventName) {
+ return SpecialPowers.spawn(aRemoteTarget, [aEventName], async (eventName) => {
+ await new Promise((aResolve) => {
+ content.document.body.addEventListener(eventName, (e) => {
+ ok(true, `got ${e.type} event on ${e.target}`);
+ aResolve();
+ }, { once: true });
+ });
+ });
+}
+
+add_task(async function test_pointer_capture_xorigin_iframe_pointer_lock() {
+ await SimpleTest.promiseFocus();
+
+ // Request pointer capture on top-level.
+ let target = document.getElementById("target");
+ synthesizeMouse(target, 10, 10, { type: "mousedown" });
+ await waitForEvent(target, "pointerdown", function(e) {
+ target.setPointerCapture(e.pointerId);
+ });
+
+ let iframe = document.querySelector("iframe");
+ synthesizeMouse(iframe, 10, 10, { type: "mousemove" });
+ await Promise.all([waitForEvent(target, "gotpointercapture"),
+ waitForEvent(target, "pointermove")]);
+
+ // Request pointer lock on iframe.
+ let iframeWin = iframe.contentWindow;
+ await Promise.all([requestPointerLockOnRemoteTarget(iframeWin, "div"),
+ waitForEvent(target, "lostpointercapture")]);
+
+ // Exit pointer lock on iframe.
+ await exitPointerLockOnRemoteTarget(iframeWin);
+
+ synthesizeMouse(target, 10, 10, { type: "mouseup" });
+ await waitForEvent(target, "pointerup");
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_touch.html b/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_touch.html
new file mode 100644
index 0000000000..f34605349f
--- /dev/null
+++ b/dom/events/test/pointerevents/file_pointercapture_xorigin_iframe_touch.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<!--https://bugzilla.mozilla.org/show_bug.cgi?id=1662587-->
+<head>
+<title>Bug 1671849 - Touch</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="pointerevent_utils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1671849">Mozilla Bug 1671849</a>
+<div id="target"></div>
+<iframe src="https://example.com/tests/dom/events/test/pointerevents/iframe.html"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1671849
+ */
+add_setup(async function() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.w3c_pointer_events.implicit_capture", true]],
+ });
+ await waitUntilApzStable();
+});
+
+add_task(async function test_pointer_capture_xorigin_iframe_touch() {
+ let iframe = document.querySelector("iframe");
+ await SpecialPowers.spawn(iframe.contentWindow, [], () => {
+ let unexpected = function(e) {
+ ok(false, `iframe shoule not get any ${e.type} event`);
+ };
+ content.document.body.addEventListener("pointermove", unexpected);
+ content.document.body.addEventListener("pointerup", unexpected);
+ });
+
+ let target = document.getElementById("target");
+ synthesizeTouch(target, 10, 10, { type: "touchstart" });
+ await waitForEvent(target, "touchstart", async function(e) {
+ e.preventDefault();
+ });
+
+ synthesizeTouch(iframe, 10, 10, { type: "touchmove" });
+ await Promise.all([waitForEvent(target, "gotpointercapture"),
+ waitForEvent(target, "pointermove")]);
+
+ synthesizeTouch(iframe, 10, 10, { type: "touchend" });
+ await Promise.all([waitForEvent(target, "lostpointercapture"),
+ waitForEvent(target, "pointerup")]);
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/file_test_trigger_fullscreen.html b/dom/events/test/pointerevents/file_test_trigger_fullscreen.html
new file mode 100644
index 0000000000..2d6549ede7
--- /dev/null
+++ b/dom/events/test/pointerevents/file_test_trigger_fullscreen.html
@@ -0,0 +1 @@
+<body><div id='target' style='width: 50px; height: 50px; background: green'></div></body>
diff --git a/dom/events/test/pointerevents/iframe.html b/dom/events/test/pointerevents/iframe.html
new file mode 100644
index 0000000000..0e3eac19b9
--- /dev/null
+++ b/dom/events/test/pointerevents/iframe.html
@@ -0,0 +1,7 @@
+<html>
+ <head>
+ </head>
+ <body>
+ <div id="div"></div><input>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/mochitest.toml b/dom/events/test/pointerevents/mochitest.toml
new file mode 100644
index 0000000000..340704f94e
--- /dev/null
+++ b/dom/events/test/pointerevents/mochitest.toml
@@ -0,0 +1,250 @@
+[DEFAULT]
+prefs = [
+ "gfx.font_loader.delay=0",
+ "dom.w3c_pointer_events.getcoalescedevents_only_in_securecontext=false"
+]
+
+support-files = [
+ "iframe.html",
+ "mochitest_support_external.js",
+ "mochitest_support_internal.js",
+ "wpt/pointerevent_styles.css",
+ "wpt/pointerevent_support.js",
+ "pointerevent_utils.js",
+ "!/gfx/layers/apz/test/mochitest/apz_test_utils.js",
+]
+
+["test_bug968148.html"]
+support-files = [
+ "bug968148_inner.html",
+ "bug968148_inner2.html",
+]
+
+["test_bug1285128.html"]
+
+["test_bug1293174_implicit_pointer_capture_for_touch_1.html"]
+support-files = ["bug1293174_implicit_pointer_capture_for_touch_1.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug1293174_implicit_pointer_capture_for_touch_2.html"]
+support-files = ["bug1293174_implicit_pointer_capture_for_touch_2.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_bug1303704.html"]
+
+["test_bug1315862.html"]
+
+["test_bug1323158.html"]
+
+["test_bug1403055.html"]
+
+["test_bug1420589_1.html"]
+support-files = [
+ "bug_1420589_iframe1.html",
+ "bug_1420589_iframe2.html",
+]
+
+["test_bug1420589_2.html"]
+support-files = ["bug_1420589_iframe1.html"]
+
+["test_bug1420589_3.html"]
+support-files = ["bug_1420589_iframe1.html"]
+
+["test_bug1725416.html"]
+skip-if = ["os == 'android'"] # Bug 1312791
+support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"]
+
+["test_getCoalescedEvents.html"]
+skip-if = [
+ "os == 'android'", # Bug 1312791
+ "display == 'wayland' && os_version == '22.04'", # Bug 1856971
+ "verify && os == 'win'", # Bug 1659744
+]
+
+["test_getCoalescedEvents_touch.html"]
+skip-if = [
+ "os == 'android'", # Bug 1312791
+ "verify && os == 'win'", # Bug 1659744
+ "win11_2009", # Bug 1781388
+]
+support-files = ["!/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"]
+
+["test_multiple_touches.html"]
+
+["test_pointercapture_remove_iframe.html"]
+
+["test_pointercapture_xorigin_iframe.html"]
+support-files = [
+ "file_pointercapture_xorigin_iframe.html",
+ "file_pointercapture_xorigin_iframe_pointerlock.html",
+ "file_pointercapture_xorigin_iframe_touch.html",
+]
+skip-if = [
+ "display == 'wayland' && os_version == '22.04'", # Bug 1856971
+]
+
+["test_pointermove_drag_scrollbar.html"]
+skip-if = ["os == 'android'"] # scrollbar not showed on mobile
+
+["test_remove_frame_when_got_pointer_capture.html"]
+
+["test_synthesized_touch.html"]
+
+["test_trigger_fullscreen_by_pointer_events.html"]
+support-files = ["file_test_trigger_fullscreen.html"]
+
+["test_wpt_pointerevent_attributes_hoverable_pointers-manual.html"]
+support-files = [
+ "wpt/pointerevent_attributes_hoverable_pointers-manual.html",
+ "wpt/resources/pointerevent_attributes_hoverable_pointers-iframe.html",
+]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_attributes_nohover_pointers-manual.html"]
+support-files = [
+ "wpt/pointerevent_attributes_nohover_pointers-manual.html",
+ "wpt/resources/pointerevent_attributes_hoverable_pointers-iframe.html",
+]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_boundary_events_in_capturing-manual.html"]
+support-files = ["wpt/pointerevent_boundary_events_in_capturing-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html"]
+support-files = ["wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html"]
+disabled = "disabled"
+
+["test_wpt_pointerevent_constructor.html"]
+support-files = ["wpt/pointerevent_constructor.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_drag_interaction-manual.html"]
+support-files = ["wpt/html/pointerevent_drag_interaction-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_movementxy-manual.html"]
+support-files = [
+ "wpt/pointerlock/pointerevent_movementxy-manual.html",
+ "wpt/pointerlock/resources/pointerevent_movementxy-iframe.html",
+]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_multiple_primary_pointers_boundary_events-manual.html"]
+support-files = ["wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html"]
+disabled = "should be investigated"
+
+["test_wpt_pointerevent_pointerId_scope-manual.html"]
+support-files = ["wpt/resources/pointerevent_pointerId_scope-iframe.html"]
+disabled = "should be investigated"
+
+["test_wpt_pointerevent_pointercancel_touch-manual.html"]
+support-files = ["wpt/pointerevent_pointercancel_touch-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_pointerleave_after_pointercancel_touch-manual.html"]
+support-files = ["wpt/pointerevent_pointerleave_after_pointercancel_touch-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_pointerleave_pen-manual.html"]
+support-files = ["wpt/pointerevent_pointerleave_pen-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_pointerout_after_pointercancel_touch-manual.html"]
+support-files = ["wpt/pointerevent_pointerout_after_pointercancel_touch-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_pointerout_pen-manual.html"]
+support-files = ["wpt/pointerevent_pointerout_pen-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_releasepointercapture_events_to_original_target-manual.html"]
+support-files = ["wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_releasepointercapture_onpointercancel_touch-manual.html"]
+support-files = ["wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_pointerevent_sequence_at_implicit_release_on_drag-manual.html"]
+support-files = ["wpt/pointerevent_sequence_at_implicit_release_on_drag-manual.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_wpt_touch_action.html"]
+skip-if = [
+ "os == 'android'", # Bug 1312791
+ "os == 'linux' && headless", # Bug 1722906
+]
+support-files = [
+ "../../../../gfx/layers/apz/test/mochitest/apz_test_utils.js",
+ "../../../../gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js",
+ "touch_action_helpers.js",
+ "wpt/pointerevent_touch-action-auto-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-button-test_touch-manual.html",
+ "wpt/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html",
+ "wpt/pointerevent_touch-action-inherit_child-none_touch-manual.html",
+ "wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html",
+ "wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html",
+ "wpt/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html",
+ "wpt/pointerevent_touch-action-inherit_parent-none_touch-manual.html",
+ "wpt/pointerevent_touch-action-none-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-x-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-x-pan-y_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-y-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-span-test_touch-manual.html",
+ "wpt/pointerevent_touch-action-svg-test_touch-manual.html",
+ "wpt/pointerevent_touch-action-table-test_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-down-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-left-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-right-css_touch-manual.html",
+ "wpt/pointerevent_touch-action-pan-up-css_touch-manual.html",
+]
diff --git a/dom/events/test/pointerevents/mochitest_support_external.js b/dom/events/test/pointerevents/mochitest_support_external.js
new file mode 100644
index 0000000000..7f22166fdd
--- /dev/null
+++ b/dom/events/test/pointerevents/mochitest_support_external.js
@@ -0,0 +1,270 @@
+// This file supports translating W3C tests
+// to tests on auto MochiTest system with minimum changes.
+// Author: Maksim Lebedev <alessarik@gmail.com>
+
+// Function allows to prepare our tests after load document
+addEventListener(
+ "load",
+ function (event) {
+ console.log("OnLoad external document");
+ prepareTest();
+ },
+ false
+);
+
+// Function allows to initialize prerequisites before testing
+function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestCompleteLog();
+ startTest();
+}
+
+function setImplicitPointerCapture(capture, callback) {
+ console.log("SET dom.w3c_pointer_events.implicit_capture as " + capture);
+ SpecialPowers.pushPrefEnv(
+ {
+ set: [["dom.w3c_pointer_events.implicit_capture", capture]],
+ },
+ callback
+ );
+}
+
+var utils = SpecialPowers.Ci.nsIDOMWindowUtils;
+
+// Mouse Event Helper Object
+var MouseEventHelper = (function () {
+ return {
+ MOUSE_ID: utils.DEFAULT_MOUSE_POINTER_ID,
+ PEN_ID: utils.DEFAULT_PEN_POINTER_ID,
+ // State
+ // TODO: Separate this to support mouse and pen simultaneously.
+ BUTTONS_STATE: utils.MOUSE_BUTTONS_NO_BUTTON,
+
+ // Button
+ BUTTON_NONE: -1, // Used by test framework only. (replaced before sending)
+ BUTTON_LEFT: utils.MOUSE_BUTTON_LEFT_BUTTON,
+ BUTTON_MIDDLE: utils.MOUSE_BUTTON_MIDDLE_BUTTON,
+ BUTTON_RIGHT: utils.MOUSE_BUTTON_RIGHT_BUTTON,
+
+ // Buttons
+ BUTTONS_NONE: utils.MOUSE_BUTTONS_NO_BUTTON,
+ BUTTONS_LEFT: utils.MOUSE_BUTTONS_LEFT_BUTTON,
+ BUTTONS_MIDDLE: utils.MOUSE_BUTTONS_MIDDLE_BUTTON,
+ BUTTONS_RIGHT: utils.MOUSE_BUTTONS_RIGHT_BUTTON,
+ BUTTONS_4TH: utils.MOUSE_BUTTONS_4TH_BUTTON,
+ BUTTONS_5TH: utils.MOUSE_BUTTONS_5TH_BUTTON,
+
+ // Utils
+ computeButtonsMaskFromButton(aButton) {
+ // Since the range of button values is 0 ~ 2 (see nsIDOMWindowUtils.idl),
+ // we can use an array to find out the desired mask.
+ var mask = [
+ this.BUTTONS_NONE, // -1 (MouseEventHelper.BUTTON_NONE)
+ this.BUTTONS_LEFT, // 0
+ this.BUTTONS_MIDDLE, // 1
+ this.BUTTONS_RIGHT, // 2
+ ][aButton + 1];
+
+ ok(mask !== undefined, "Unrecognized button value caught!");
+ return mask;
+ },
+
+ checkExitState() {
+ ok(!this.BUTTONS_STATE, "Mismatched mousedown/mouseup caught.");
+ },
+ };
+})();
+
+function createMouseEvent(aEventType, aParams) {
+ var eventObj = { type: aEventType };
+
+ // Default to mouse.
+ eventObj.inputSource =
+ aParams && "inputSource" in aParams
+ ? aParams.inputSource
+ : MouseEvent.MOZ_SOURCE_MOUSE;
+ // Compute pointerId
+ eventObj.id =
+ eventObj.inputSource === MouseEvent.MOZ_SOURCE_MOUSE
+ ? MouseEventHelper.MOUSE_ID
+ : MouseEventHelper.PEN_ID;
+ // Check or generate a |button| value.
+ var isButtonEvent = aEventType === "mouseup" || aEventType === "mousedown";
+
+ // Set |button| to the default value first.
+ eventObj.button = isButtonEvent
+ ? MouseEventHelper.BUTTON_LEFT
+ : MouseEventHelper.BUTTON_NONE;
+
+ // |button| is passed, use and check it.
+ if (aParams && "button" in aParams) {
+ var hasButtonValue = aParams.button !== MouseEventHelper.BUTTON_NONE;
+ ok(
+ !isButtonEvent || hasButtonValue,
+ "Inappropriate |button| value caught."
+ );
+ eventObj.button = aParams.button;
+ }
+
+ // Generate a |buttons| value and update buttons state
+ var buttonsMask = MouseEventHelper.computeButtonsMaskFromButton(
+ eventObj.button
+ );
+ switch (aEventType) {
+ case "mousedown":
+ MouseEventHelper.BUTTONS_STATE |= buttonsMask; // Set button flag.
+ break;
+ case "mouseup":
+ MouseEventHelper.BUTTONS_STATE &= ~buttonsMask; // Clear button flag.
+ break;
+ }
+ eventObj.buttons = MouseEventHelper.BUTTONS_STATE;
+
+ // Replace the button value for mousemove events.
+ // Since in widget level design, even when no button is pressed at all, the
+ // value of WidgetMouseEvent.button is still 0, which is the same value as
+ // the one for mouse left button.
+ if (aEventType === "mousemove") {
+ eventObj.button = MouseEventHelper.BUTTON_LEFT;
+ }
+ return eventObj;
+}
+
+// Helper function to send MouseEvent with different parameters
+function sendMouseEvent(int_win, elemId, mouseEventType, params) {
+ var elem = int_win.document.getElementById(elemId);
+ if (elem) {
+ var rect = elem.getBoundingClientRect();
+ var eventObj = createMouseEvent(mouseEventType, params);
+
+ // Default to the center of the target element but we can still send to a
+ // position outside of the target element.
+ var offsetX =
+ params && "offsetX" in params ? params.offsetX : rect.width / 2;
+ var offsetY =
+ params && "offsetY" in params ? params.offsetY : rect.height / 2;
+
+ console.log(elemId, eventObj);
+ synthesizeMouse(elem, offsetX, offsetY, eventObj, int_win);
+ } else {
+ is(!!elem, true, "Document should have element with id: " + elemId);
+ }
+}
+
+// Helper function to send MouseEvent with position
+function sendMouseEventAtPoint(aWindow, aLeft, aTop, aMouseEventType, aParams) {
+ var eventObj = createMouseEvent(aMouseEventType, aParams);
+ console.log(eventObj);
+ synthesizeMouseAtPoint(aLeft, aTop, eventObj, aWindow);
+}
+
+// Touch Event Helper Object
+var TouchEventHelper = {
+ // State
+ TOUCH_ID: utils.DEFAULT_TOUCH_POINTER_ID,
+ TOUCH_STATE: false,
+
+ // Utils
+ checkExitState() {
+ ok(!this.TOUCH_STATE, "Mismatched touchstart/touchend caught.");
+ },
+};
+
+// Helper function to send TouchEvent with different parameters
+// TODO: Support multiple touch points to test more features such as
+// PointerEvent.isPrimary and pinch-zoom.
+function sendTouchEvent(int_win, elemId, touchEventType, params) {
+ var elem = int_win.document.getElementById(elemId);
+ if (elem) {
+ var rect = elem.getBoundingClientRect();
+ var eventObj = {
+ type: touchEventType,
+ id: TouchEventHelper.TOUCH_ID,
+ };
+
+ // Update touch state
+ switch (touchEventType) {
+ case "touchstart":
+ TouchEventHelper.TOUCH_STATE = true; // Set touch flag.
+ break;
+ case "touchend":
+ case "touchcancel":
+ TouchEventHelper.TOUCH_STATE = false; // Clear touch flag.
+ break;
+ }
+
+ // Default to the center of the target element but we can still send to a
+ // position outside of the target element.
+ var offsetX =
+ params && "offsetX" in params ? params.offsetX : rect.width / 2;
+ var offsetY =
+ params && "offsetY" in params ? params.offsetY : rect.height / 2;
+
+ console.log(elemId, eventObj);
+ synthesizeTouch(elem, offsetX, offsetY, eventObj, int_win);
+ } else {
+ is(!!elem, true, "Document should have element with id: " + elemId);
+ }
+}
+
+// Helper function to trigger drag and drop.
+async function doDragAndDrop(int_win, srcElemId, destElemId, params = {}) {
+ params.srcElement = int_win.document.getElementById(srcElemId);
+ params.destElement = int_win.document.getElementById(destElemId);
+ params.srcWindow = int_win;
+ params.destWindow = int_win;
+ params.id = MouseEventHelper.MOUSE_ID;
+ // This is basically for android which has a larger drag threshold.
+ params.stepY = params.stepY || 25;
+ await synthesizePlainDragAndDrop(params);
+}
+
+// Helper function to run Point Event test in a new tab.
+function runTestInNewWindow(aFile) {
+ var testURL =
+ location.href.substring(0, location.href.lastIndexOf("/") + 1) + aFile;
+ var testWindow = window.open(testURL, "_blank");
+ var testDone = false;
+
+ // We start testing when receiving load event. Inject the mochitest helper js
+ // to the test case after DOM elements are constructed and before the load
+ // event is fired.
+ testWindow.addEventListener(
+ "DOMContentLoaded",
+ function () {
+ var e = testWindow.document.createElement("script");
+ e.type = "text/javascript";
+ e.src =
+ "../".repeat(aFile.split("/").length - 1) +
+ "mochitest_support_internal.js";
+ testWindow.document.getElementsByTagName("head")[0].appendChild(e);
+ },
+ { once: true }
+ );
+
+ window.addEventListener("message", function (aEvent) {
+ switch (aEvent.data.type) {
+ case "START":
+ // Update constants
+ MouseEventHelper.MOUSE_ID = aEvent.data.message.mouseId;
+ MouseEventHelper.PEN_ID = aEvent.data.message.penId;
+ TouchEventHelper.TOUCH_ID = aEvent.data.message.touchId;
+
+ executeTest(testWindow);
+ break;
+ case "RESULT":
+ // Should not perform checking after SimpleTest.finish().
+ if (!testDone) {
+ ok(aEvent.data.result, aEvent.data.message);
+ }
+ break;
+ case "FIN":
+ testDone = true;
+ MouseEventHelper.checkExitState();
+ TouchEventHelper.checkExitState();
+ testWindow.close();
+ SimpleTest.finish();
+ break;
+ }
+ });
+}
diff --git a/dom/events/test/pointerevents/mochitest_support_internal.js b/dom/events/test/pointerevents/mochitest_support_internal.js
new file mode 100644
index 0000000000..d46dd14f32
--- /dev/null
+++ b/dom/events/test/pointerevents/mochitest_support_internal.js
@@ -0,0 +1,125 @@
+// This file supports translating W3C tests
+// to tests on auto MochiTest system with minimum changes.
+// Author: Maksim Lebedev <alessarik@gmail.com>
+
+const PARENT_ORIGIN = "http://mochi.test:8888/";
+
+// Since web platform tests don't check pointerId, we have to use some heuristic
+// to test them. and thus pointerIds are send to mochitest_support_external.js
+// before we start sending synthesized widget events. Here, we avoid using
+// default values used in Gecko to insure everything works as expected.
+const POINTER_MOUSE_ID = 7;
+const POINTER_PEN_ID = 8;
+const POINTER_TOUCH_ID = 9; // Extend for multiple touch points if needed.
+
+// Setup environment.
+addListeners(document.getElementById("target0"));
+addListeners(document.getElementById("target1"));
+
+// Setup communication between mochitest_support_external.js.
+// Function allows to initialize prerequisites before testing
+// and adds some callbacks to support mochitest system.
+function resultCallback(aTestObj) {
+ var message = aTestObj.name + " (";
+ message += "Get: " + JSON.stringify(aTestObj.status) + ", ";
+ message += "Expect: " + JSON.stringify(aTestObj.PASS) + ")";
+ window.opener.postMessage(
+ {
+ type: "RESULT",
+ message,
+ result: aTestObj.status === aTestObj.PASS,
+ },
+ PARENT_ORIGIN
+ );
+}
+
+add_result_callback(resultCallback);
+add_completion_callback(() => {
+ window.opener.postMessage({ type: "FIN" }, PARENT_ORIGIN);
+});
+
+window.addEventListener("load", () => {
+ // Start testing.
+ var startMessage = {
+ type: "START",
+ message: {
+ mouseId: POINTER_MOUSE_ID,
+ penId: POINTER_PEN_ID,
+ touchId: POINTER_TOUCH_ID,
+ },
+ };
+ window.opener.postMessage(startMessage, PARENT_ORIGIN);
+});
+
+function addListeners(elem) {
+ if (!elem) {
+ return;
+ }
+ var All_Events = [
+ "pointerdown",
+ "pointerup",
+ "pointercancel",
+ "pointermove",
+ "pointerover",
+ "pointerout",
+ "pointerenter",
+ "pointerleave",
+ "gotpointercapture",
+ "lostpointercapture",
+ ];
+ All_Events.forEach(function (name) {
+ elem.addEventListener(name, function (event) {
+ console.log("(" + event.type + ")-(" + event.pointerType + ")");
+
+ // Perform checks only for trusted events.
+ if (!event.isTrusted) {
+ return;
+ }
+
+ // Compute the desired event.pointerId from event.pointerType.
+ var pointerId = {
+ mouse: POINTER_MOUSE_ID,
+ pen: POINTER_PEN_ID,
+ touch: POINTER_TOUCH_ID,
+ }[event.pointerType];
+
+ // Compare the pointerId.
+ resultCallback({
+ name: "Mismatched event.pointerId recieved.",
+ status: event.pointerId,
+ PASS: pointerId,
+ });
+ });
+ });
+}
+
+// mock the touchScrollInTarget to make the test work.
+function touchScrollInTarget() {
+ return Promise.resolve();
+}
+
+// mock test_driver to make the test work.
+function Actions() {}
+Actions.prototype = {
+ addPointer() {
+ return this;
+ },
+ pointerMove() {
+ return this;
+ },
+ pointerDown() {
+ return this;
+ },
+ pause() {
+ return this;
+ },
+ pointerUp() {
+ return this;
+ },
+ send() {
+ return Promise.resolve();
+ },
+};
+const test_driver = {
+ Actions,
+};
diff --git a/dom/events/test/pointerevents/pointerevent_utils.js b/dom/events/test/pointerevents/pointerevent_utils.js
new file mode 100644
index 0000000000..ac5eaf61a1
--- /dev/null
+++ b/dom/events/test/pointerevents/pointerevent_utils.js
@@ -0,0 +1,60 @@
+// Get test filename for page being run in popup so errors are more useful
+var testName = location.pathname.split("/").pop();
+
+// Wrap test functions and pass to parent window
+window.ok = function (a, msg) {
+ opener.ok(a, testName + ": " + msg);
+};
+
+window.is = function (a, b, msg) {
+ opener.is(a, b, testName + ": " + msg);
+};
+
+window.isnot = function (a, b, msg) {
+ opener.isnot(a, b, testName + ": " + msg);
+};
+
+window.todo = function (a, msg) {
+ opener.todo(a, testName + ": " + msg);
+};
+
+window.todo_is = function (a, b, msg) {
+ opener.todo_is(a, b, testName + ": " + msg);
+};
+
+window.todo_isnot = function (a, b, msg) {
+ opener.todo_isnot(a, b, testName + ": " + msg);
+};
+
+window.info = function (msg) {
+ opener.info(testName + ": " + msg);
+};
+
+// Override bits of SimpleTest so test files work stand-alone
+var SimpleTest = SimpleTest || {};
+
+SimpleTest.waitForExplicitFinish = function () {
+ dump("[POINTEREVENT] Starting " + testName + "\n");
+};
+
+SimpleTest.finish = function () {
+ dump("[POINTEREVENT] Finishing " + testName + "\n");
+ opener.nextTest();
+};
+
+// Utility functions
+function waitForEvent(aTarget, aEvent, aCallback) {
+ return new Promise(aResolve => {
+ aTarget.addEventListener(
+ aEvent,
+ e => {
+ ok(true, `got ${e.type} event on ${e.target.id}`);
+ if (aCallback) {
+ aCallback(e);
+ }
+ aResolve();
+ },
+ { once: true }
+ );
+ });
+}
diff --git a/dom/events/test/pointerevents/readme.md b/dom/events/test/pointerevents/readme.md
new file mode 100644
index 0000000000..0cc0190979
--- /dev/null
+++ b/dom/events/test/pointerevents/readme.md
@@ -0,0 +1,9 @@
+Directory for Pointer Events Tests
+
+Latest Editor's Draft: https://w3c.github.io/pointerevents/
+
+Latest W3C Technical Report: http://www.w3.org/TR/pointerevents/
+
+Discussion forum for tests: http://lists.w3.org/Archives/Public/public-test-infra/
+
+Test Assertion table: https://www.w3.org/wiki/PointerEvents/TestAssertions
diff --git a/dom/events/test/pointerevents/test_bug1285128.html b/dom/events/test/pointerevents/test_bug1285128.html
new file mode 100644
index 0000000000..534e265047
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1285128.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1285128
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1285128</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=1285128">Mozilla Bug 1285128</a>
+<p id="display"></p>
+<div id="target0" style="width: 50px; height: 50px; background: green"></div>
+<div id="target1" style="width: 50px; height: 50px; background: red"></div>
+<script type="text/javascript">
+
+/** Test for Bug 1285128 **/
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ let target0 = window.document.getElementById("target0");
+ let pointerEventsList = ["pointerover", "pointerenter", "pointerdown",
+ "pointerup", "pointerleave", "pointerout"];
+ let receivedPointerEvents = false;
+ pointerEventsList.forEach((elem, index, arr) => {
+ target0.addEventListener(elem, (event) => {
+ ok(false, "receiving event " + event.type);
+ receivedPointerEvents = true;
+ });
+ });
+
+ target1.addEventListener("mouseup", () => {
+ ok(!receivedPointerEvents, "synthesized mousemove should not trigger any pointer events");
+ SimpleTest.finish();
+ });
+
+ synthesizeMouseAtCenter(target0, { type: "mousemove",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE,
+ isWidgetEventSynthesized: true });
+ synthesizeMouseAtCenter(target1, { type: "mousedown" });
+ synthesizeMouseAtCenter(target1, { type: "mouseup" });
+}
+
+SimpleTest.waitForFocus(runTests);
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html
new file mode 100644
index 0000000000..a02432f3b3
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1293174</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ setImplicitPointerCapture(true, loadSubFrame);
+ }
+ function loadSubFrame() {
+ runTestInNewWindow("bug1293174_implicit_pointer_capture_for_touch_1.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchmove");
+ sendTouchEvent(int_win, "target1", "touchmove");
+ sendTouchEvent(int_win, "target0", "touchmove");
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
+
diff --git a/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html
new file mode 100644
index 0000000000..2e5aaccccc
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1293174_implicit_pointer_capture_for_touch_2.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1293174</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ setImplicitPointerCapture(false, loadSubFrame);
+ }
+ function loadSubFrame() {
+ runTestInNewWindow("bug1293174_implicit_pointer_capture_for_touch_2.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchmove");
+ sendTouchEvent(int_win, "target1", "touchmove");
+ sendTouchEvent(int_win, "target0", "touchmove");
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
+
diff --git a/dom/events/test/pointerevents/test_bug1303704.html b/dom/events/test/pointerevents/test_bug1303704.html
new file mode 100644
index 0000000000..ed8842997c
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1303704.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1303704
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1303704</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"/>
+ <style>
+ #scrollable {
+ height: 80px;
+ width: 200px;
+ overflow-y: scroll;
+ margin-bottom: 50px;
+ scroll-behavior: auto;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1303704">Mozilla Bug 1303704</a>
+<p id="display"></p>
+<a id="link1" href="http://www.google.com">Link 1</a>
+<div id="scrollable">
+<pre>
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+scroll
+</pre>
+</div>
+<script type="text/javascript">
+
+/** Test for Bug 1303704 **/
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ let link1 = window.document.getElementById("link1");
+ let mouseEvents = ["mousedown", "mouseup", "mousemove"];
+
+ link1.addEventListener("pointerdown", (e) => {
+ e.preventDefault();
+ is(e.defaultPrevented, true, "defaultPrevented should be true");
+ });
+
+ mouseEvents.forEach((elm, index, arr) => {
+ link1.addEventListener(elm, () => {
+ ok(false, "Should not receive " + elm + " after preventDefault on pointerdown");
+ });
+ });
+
+ link1.addEventListener("click", (e) => {
+ e.preventDefault();
+ });
+
+ synthesizeMouseAtCenter(link1, { type: "mousedown",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE });
+ synthesizeMouseAtCenter(link1, { type: "mousemove",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE });
+ synthesizeMouseAtCenter(link1, { type: "mouseup",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE });
+
+ if (navigator.userAgent.includes("Android") ||
+ navigator.userAgent.includes("Mac") ||
+ SpecialPowers.Cc["@mozilla.org/gfx/info;1"].
+ getService(SpecialPowers.Ci.nsIGfxInfo).isHeadless) {
+ SimpleTest.finish();
+ return;
+ }
+
+ async function scrollTest() {
+ var scrollable = document.getElementById("scrollable");
+ scrollable.addEventListener('pointerdown', function(ev) {
+ ev.preventDefault();
+ }, true);
+ var rect = scrollable.getBoundingClientRect();
+ var offsetX = rect.width - 5;
+ var offsetY = rect.height - 5;
+ synthesizeMouse(scrollable, offsetX, offsetY,
+ { type: "mousedown",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE });
+
+ synthesizeMouse(scrollable, offsetX, offsetY,
+ { type: "mousemove",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE });
+
+ synthesizeMouse(scrollable, offsetX, offsetY,
+ { type: "mouseup",
+ inputSource: MouseEvent.MOZ_SOURCE_MOUSE });
+
+ await waitToClearOutAnyPotentialScrolls(window);
+
+ if (scrollable.scrollTop != 0) {
+ isnot(scrollable.scrollTop, 0,
+ "Scrollable element should have been scrolled.");
+ SimpleTest.finish();
+ } else {
+ setTimeout(scrollTest);
+ }
+ }
+
+ setTimeout(() => {
+ var scrollable = document.getElementById("scrollable");
+ is(scrollable.scrollTop, 0,
+ "Scrollable element shouldn't be scrolled initially");
+ scrollTest();
+ });
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]}, runTests);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1315862.html b/dom/events/test/pointerevents/test_bug1315862.html
new file mode 100644
index 0000000000..92d61a518f
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1315862.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1315862
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1315862</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>
+<p id="content">
+ This is a test to check if pointer events are dispatched in the system group
+</p>
+<script type="text/javascript">
+
+/** Test for Bug 1315862 **/
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ let allPointerEvents = ["pointerdown", "pointerup", "pointercancel",
+ "pointermove", "pointerover", "pointerout",
+ "pointerenter", "pointerleave", "gotpointercapture",
+ "lostpointercapture"
+ ];
+ let content = document.getElementById('content');
+ let iframe = document.createElement('iframe');
+ let receivePointerEvents = false;
+ iframe.width = 50;
+ iframe.height = 50;
+ content.appendChild(iframe);
+ iframe.contentDocument.body.innerHTML =
+ "<div style='width: 100%; height: 100%; border: 1px solid black;'></div>";
+
+ let target = iframe.contentDocument.body.firstChild;
+ allPointerEvents.forEach((event, idx, arr) => {
+ SpecialPowers.addSystemEventListener(target, event, () => {
+ ok(false, "Shouldn't dispatch " + event + " in the system group");
+ receivePointerEvents = true;
+ });
+ });
+ target.addEventListener("pointerdown", (e) => {
+ target.setPointerCapture(e.pointerId);
+ });
+ target.addEventListener("pointerup", () => {
+ is(receivePointerEvents, false, "Shouldn't dispatch pointer events in the system group");
+ SimpleTest.finish();
+ });
+ let source = MouseEvent.MOZ_SOURCE_MOUSE;
+ synthesizeMouse(target, 5, 5, { type: "mousemove", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target, 5, 5, { type: "mousedown", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target, 5, 5, { type: "mousemove", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target, 5, 5, { type: "mouseup", inputSource: source },
+ iframe.contentWindow);
+}
+
+SimpleTest.waitForFocus(runTests);
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1323158.html b/dom/events/test/pointerevents/test_bug1323158.html
new file mode 100644
index 0000000000..96fd2de12e
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1323158.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1323158
+-->
+<head>
+ <meta charset="utf-8">
+ <title>This is a test to check if target and relatedTarget of mouse events are the same as pointer 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>
+<p id="content"></p>
+<script type="text/javascript">
+
+/** Test for Bug 1323158 **/
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ let content = document.getElementById('content');
+ let iframe = document.createElement('iframe');
+ iframe.width = 500;
+ iframe.height = 500;
+ content.appendChild(iframe);
+ iframe.contentDocument.body.innerHTML =
+ "<br><div id='target0' style='width: 30px; height: 30px; background: black;'></div>" +
+ "<br><div id='target1' style='width: 30px; height: 30px; background: red;'></div>" +
+ "<br><div id='done' style='width: 30px; height: 30px; background: green;'></div>";
+
+ let target0 = iframe.contentDocument.getElementById("target0");
+ let target1 = iframe.contentDocument.getElementById("target1");
+ let done = iframe.contentDocument.getElementById("done");
+ let pointerBoundaryEvents = ["pointerover", "pointerenter", "pointerleave", "pointerout"];
+ let mouseBoundaryEvents = ["mouseover", "mouseenter", "mouseleave", "mouseout"];
+ let receivedPointerBoundaryEvents = {};
+ let mouseEvent2pointerEvent = {"mouseover": "pointerover",
+ "mouseenter": "pointerenter",
+ "mouseleave": "pointerleave",
+ "mouseout": "pointerout"
+ };
+
+ pointerBoundaryEvents.forEach((event) => {
+ target1.addEventListener(event, (e) => {
+ receivedPointerBoundaryEvents[event] = e;
+ }, {once: true});
+ });
+
+ let attributes = ["target", "relatedTarget"];
+ mouseBoundaryEvents.forEach((event) => {
+ target1.addEventListener(event, (e) => {
+ let correspondingPointerEv = receivedPointerBoundaryEvents[mouseEvent2pointerEvent[event]];
+ ok(correspondingPointerEv, "Should receive " + mouseEvent2pointerEvent[event] + " before " + e.type);
+ if (correspondingPointerEv) {
+ attributes.forEach((attr) => {
+ ok(correspondingPointerEv[attr] == e[attr],
+ attr + " of " + e.type + " should be the same as the correcponding pointer event expect " +
+ correspondingPointerEv[attr] + " got " + e[attr]);
+ });
+ }
+ receivedPointerBoundaryEvents[mouseEvent2pointerEvent[event]] = null;
+ }, {once: true});
+ });
+ target0.addEventListener("pointerdown", (e) => {
+ target1.setPointerCapture(e.pointerId);
+ });
+ done.addEventListener("mouseup", () => {
+ SimpleTest.finish();
+ });
+ let source = MouseEvent.MOZ_SOURCE_MOUSE;
+ synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target0, 5, 5, { type: "mousedown", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target0, 5, 5, { type: "mouseup", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(target0, 5, 5, { type: "mousemove", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(done, 5, 5, { type: "mousedown", inputSource: source },
+ iframe.contentWindow);
+ synthesizeMouse(done, 5, 5, { type: "mouseup", inputSource: source },
+ iframe.contentWindow);
+}
+
+SimpleTest.waitForFocus(runTests);
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1403055.html b/dom/events/test/pointerevents/test_bug1403055.html
new file mode 100644
index 0000000000..8327fd147d
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1403055.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1403055
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1403055</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=1403055">Mozilla Bug 1403055</a>
+<p id="display"></p>
+<div id="target0" style="width: 50px; height: 50px; background: green"></div>
+<div id="target1" style="width: 50px; height: 50px; background: red"></div>
+<div id="done" style="width: 50px; height: 50px; background: black"></div>
+<script type="text/javascript">
+/** Test for Bug 1403055 **/
+SimpleTest.waitForExplicitFinish();
+
+var target0 = window.document.getElementById("target0");
+var target1 = window.document.getElementById("target1");
+var done = window.document.getElementById("done");
+
+function withoutImplicitlyPointerCaptureForTouch() {
+ let target0_events = ["pointerover", "pointerenter", "pointerdown", "pointermove"];
+ let target1_events = ["pointerover", "pointerenter", "pointermove", "pointercancel", "pointerout", "pointerleave"];
+
+ target0_events.forEach((elem, index, arr) => {
+ target0.addEventListener(elem, (event) => {
+ is(event.type, target0_events[0], "receive " + event.type + " on target0");
+ target0_events = target0_events.filter(item => item !== event.type);
+ }, { once: true });
+ });
+
+ target1_events.forEach((elem, index, arr) => {
+ target1.addEventListener(elem, (event) => {
+ is(event.type, target1_events[0], "receive " + event.type + " on target1");
+ target1_events = target1_events.filter(item => item !== event.type);
+ }, { once: true });
+ });
+
+ done.addEventListener("mouseup", () => {
+ ok(!target0_events.length, " should receive " + target0_events + " on target0");
+ ok(!target1_events.length, " should receive " + target1_events + " on target1");
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.implicit_capture", true]]},
+ withImplicitlyPointerCaptureForTouch);
+ }, {once : true});
+
+ synthesizeTouch(target0, 5, 5, { type: "touchstart" });
+ synthesizeTouch(target0, 6, 6, { type: "touchmove" });
+ synthesizeTouch(target1, 5, 5, { type: "touchmove" });
+ synthesizeTouch(target1, 5, 5, { type: "touchcancel" });
+ synthesizeMouseAtCenter(done, { type: "mousedown" });
+ synthesizeMouseAtCenter(done, { type: "mouseup" });
+}
+
+function withImplicitlyPointerCaptureForTouch() {
+ let target0_events = ["pointerover", "pointerenter", "pointerdown", "pointermove", "pointercancel", "pointerout", "pointerleave"];
+
+ target0_events.forEach((elem, index, arr) => {
+ target0.addEventListener(elem, (event) => {
+ is(event.type, target0_events[0], "receive " + event.type + " on target0");
+ target0_events = target0_events.filter(item => item !== event.type);
+ }, { once: true });
+ });
+
+ done.addEventListener("mouseup", () => {
+ ok(!target0_events.length, " should receive " + target0_events + " on target0");
+ SimpleTest.finish();
+ }, {once : true});
+
+ synthesizeTouch(target0, 5, 5, { type: "touchstart" });
+ synthesizeTouch(target0, 5, 5, { type: "touchmove" });
+ synthesizeTouch(target1, 5, 5, { type: "touchmove" });
+ synthesizeTouch(target1, 5, 5, { type: "touchcancel" });
+ synthesizeMouseAtCenter(done, { type: "mousedown" });
+ synthesizeMouseAtCenter(done, { type: "mouseup" });
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.implicit_capture", false]]},
+ withoutImplicitlyPointerCaptureForTouch);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1420589_1.html b/dom/events/test/pointerevents/test_bug1420589_1.html
new file mode 100644
index 0000000000..8ecea7a28b
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1420589_1.html
@@ -0,0 +1,105 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1420589
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1420589</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=1420589">Mozilla Bug 1420589</a>
+<p id="display"></p>
+<iframe id="iframe1" src="./bug_1420589_iframe1.html">
+</iframe>
+<iframe id="iframe2" src="./bug_1420589_iframe2.html">
+</iframe>
+<script type="text/javascript">
+/*
+ Test for Bug 1420589. This test synthesizes touch events with two points. The
+ first one hits iframe1 and the other hits iframe2.
+
+ We dispatch all touch events to the same document. We stop dispatching touch
+ events to a target if we can't find any ancestor document that is the same as
+ the document of the existing target. We check the points of the touch event in
+ reverse order. That means we choose the document of iframe2 as our targeted
+ document. We won't dispatch touch events to the document of iframe1 nor the
+ parent document of iframe1 and iframe2.
+
+ We dispatch pointer events to the hit targets even when there aren't in the
+ same document. This test expects that pointer events are dispatched to the div
+ element and the iframe document.
+*/
+SimpleTest.waitForExplicitFinish();
+
+var rx = 1;
+var ry = 1;
+var angle = 0;
+var force = 1;
+var modifiers = 0;
+var test1PointerId = 1;
+var test2PointerId = 2;
+
+function withoutImplicitlyPointerCaptureForTouch() {
+ let expectedEvents = [
+ // messages from the document of iframe1
+ "iframe1 pointerdown",
+ "iframe1 pointermove",
+ "iframe1 pointerup",
+
+ // messages from the document of iframe2
+ "iframe2 pointerdown",
+ "iframe2 pointermove",
+ "iframe2 pointerup",
+ "iframe2 touchstart",
+ "iframe2 touchmove",
+ "iframe2 touchend",
+ ];
+
+ window.addEventListener('message',function(e) {
+ ok(expectedEvents.includes(e.data), " don't expect " + e.data);
+ expectedEvents = expectedEvents.filter(item => item !== e.data);
+ if (e.data == "iframe2 touchend") {
+ ok(!expectedEvents.length, " expect " + expectedEvents);
+ SimpleTest.finish();
+ }
+ })
+
+ let iframe1 = document.getElementById('iframe1');
+ let iframe2 = document.getElementById('iframe2');
+
+ let rect1 = iframe1.getBoundingClientRect();
+ let rect2 = iframe2.getBoundingClientRect();
+
+ let left1 = rect1.left + 5;
+ let left2 = rect2.left + 5;
+
+ let top1 = rect1.top + 5;
+ let top2 = rect2.top + 5;
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1 + 1, left2 + 1], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1 + 1, left2 + 1], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.implicit_capture", false]]},
+ withoutImplicitlyPointerCaptureForTouch);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1420589_2.html b/dom/events/test/pointerevents/test_bug1420589_2.html
new file mode 100644
index 0000000000..2bfac2cdfc
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1420589_2.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1420589
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1420589</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=1420589">Mozilla Bug 1420589</a>
+<p id="display"></p>
+<div id="div1" style="width: 50px; height: 50px; background: green"></div>
+<iframe id="iframe1" src="./bug_1420589_iframe1.html">
+</iframe>
+<script type="text/javascript">
+/*
+ Test for Bug 1420589. This test synthesizes touch events with two points. One
+ hits the div element on the document and the other hits the iframe element.
+
+ We dispatch all touch events to the same document. If we find any target that
+ is not in the same document of the existed target, we try to find the ancestor
+ document of the new target which is in the same as the existing target and
+ dispatch touch events to it. We check the points of the touch event in reverse
+ order. That means we only dispatch touch events to the document which contains
+ the div element in this test and expect the div element and iframe element
+ receive touch events.
+
+ We dispatch pointer events to the hit targets even when there aren't in the
+ same document. This test expects that pointer events are dispatched to the div
+ element and the iframe document.
+*/
+SimpleTest.waitForExplicitFinish();
+
+var rx = 1;
+var ry = 1;
+var angle = 0;
+var force = 1;
+var modifiers = 0;
+var test1PointerId = 1;
+var test2PointerId = 2;
+
+function withoutImplicitlyPointerCaptureForTouch() {
+ let expectedEvents = [
+ // messages from the document of iframe1
+ "iframe1 pointerdown",
+ "iframe1 pointermove",
+ "iframe1 pointerup",
+
+ // messages from the parent document
+ "iframe touchstart",
+ "iframe touchmove",
+ "iframe touchend",
+ "div1 pointerdown",
+ "div1 pointermove",
+ "div1 pointerup",
+ "div1 touchstart",
+ "div1 touchmove",
+ "div1 touchend",
+ ];
+
+ window.addEventListener('message',function(e) {
+ ok(expectedEvents.includes(e.data), " don't expect " + e.data);
+ expectedEvents = expectedEvents.filter(item => item !== e.data);
+ if (e.data == "div1 touchend") {
+ ok(!expectedEvents.length, " expect " + expectedEvents);
+ SimpleTest.finish();
+ }
+ })
+
+ let iframe1 = document.getElementById('iframe1');
+ let div1 = document.getElementById('div1');
+
+ let events = ["touchstart", "touchmove", "touchend", "pointerdown", "pointermove", "pointerup"];
+ events.forEach((event) => {
+ div1.addEventListener(event, (e) => {
+ postMessage("div1 " + e.type, "*");
+ }, { once: true });
+ iframe1.addEventListener(event, (e) => {
+ postMessage("iframe " + e.type, "*");
+ }, { once: true });
+ });
+
+ let rect1 = iframe1.getBoundingClientRect();
+ let rect2 = div1.getBoundingClientRect();
+
+ let left1 = rect1.left + 5;
+ let left2 = rect2.left + 5;
+
+ let top1 = rect1.top + 5;
+ let top2 = rect2.top + 5;
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Move the touch pointers so that we dispatch all of them to content.
+ left1++;
+ left2++;
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.implicit_capture", false]]},
+ withoutImplicitlyPointerCaptureForTouch);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1420589_3.html b/dom/events/test/pointerevents/test_bug1420589_3.html
new file mode 100644
index 0000000000..9942591f64
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1420589_3.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1420589
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1420589</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=1420589">Mozilla Bug 1420589</a>
+<p id="display"></p>
+<div id="div1" style="width: 50px; height: 50px; background: green"></div>
+<iframe id="iframe1" src="./bug_1420589_iframe1.html">
+</iframe>
+<script type="text/javascript">
+/*
+ Test for Bug 1420589. This test is similar to test_bug1420589_2.html but the
+ first touch point hit the div element and the second point hits the iframe.
+
+ We stop dispatching touch events to a target when we can't find any ancestor
+ document that is the same as the document of the existing target. This test
+ expects that we only dispatch touch events to the iframe document.
+
+ We dispatch pointer events to the hit targets even when there aren't in the
+ same document. This test expects that pointer events are dispatched to the div
+ element and the iframe document.
+*/
+SimpleTest.waitForExplicitFinish();
+
+var rx = 1;
+var ry = 1;
+var angle = 0;
+var force = 1;
+var modifiers = 0;
+var test1PointerId = 1;
+var test2PointerId = 2;
+
+function withoutImplicitlyPointerCaptureForTouch() {
+ let expectedEvents = [
+ // messages from the document of iframe1
+ "iframe1 pointerdown",
+ "iframe1 pointermove",
+ "iframe1 pointerup",
+ "iframe1 touchstart",
+ "iframe1 touchmove",
+ "iframe1 touchend",
+
+ // messages from the parent document
+ "div1 pointerdown",
+ "div1 pointermove",
+ "div1 pointerup",
+ ];
+
+ window.addEventListener('message',function(e) {
+ ok(expectedEvents.includes(e.data), " don't expect " + e.data);
+ expectedEvents = expectedEvents.filter(item => item !== e.data);
+ if (e.data == "iframe1 touchend") {
+ ok(!expectedEvents.length, " expect " + expectedEvents);
+ SimpleTest.finish();
+ }
+ })
+
+ let iframe1 = document.getElementById('iframe1');
+ let div1 = document.getElementById('div1');
+
+ let events = ["touchstart", "touchmove", "touchend", "pointerdown", "pointermove", "pointerup"];
+ events.forEach((event) => {
+ div1.addEventListener(event, (e) => {
+ postMessage("div1 " + e.type, "*");
+ }, { once: true });
+ iframe1.addEventListener(event, (e) => {
+ postMessage("iframe " + e.type, "*");
+ }, { once: true });
+ });
+
+ let rect1 = div1.getBoundingClientRect();
+ let rect2 = iframe1.getBoundingClientRect();
+
+ let left1 = rect1.left + 5;
+ let left2 = rect2.left + 5;
+
+ let top1 = rect1.top + 5;
+ let top2 = rect2.top + 5;
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendTouchEvent('touchstart', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+
+ // Move the touch pointers so that we dispatch all of them to content.
+ left1++;
+ left2++;
+ utils.sendTouchEvent('touchmove', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+ utils.sendTouchEvent('touchend', [test1PointerId, test2PointerId],
+ [left1, left2], [top1, top2], [rx, rx], [ry, ry],
+ [angle, angle], [force, force], [0, 0], [0, 0],
+ [0, 0], modifiers);
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_pointer_events.implicit_capture", false]]},
+ withoutImplicitlyPointerCaptureForTouch);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_bug1697769.xhtml b/dom/events/test/pointerevents/test_bug1697769.xhtml
new file mode 100644
index 0000000000..09efdb9826
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1697769.xhtml
@@ -0,0 +1,74 @@
+<?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 xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ title="Test for Bug 1697769">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1697769
+-->
+<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+<script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+<hbox height="300" align="center">
+<vbox width="30" align="center">
+<scrollbar id="scrollbar" orient="vertical" curpos="0" maxpos="500" style="height: 300px" />
+</vbox>
+</hbox>
+
+<body id="html_body" xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1697769">Mozilla Bug 1697769</a>
+<p id="display"></p>
+<pre id="test"></pre>
+</body>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+let scrollbar;
+let scrollbarThumb;
+
+add_setup(async function() {
+ scrollbar = document.getElementById("scrollbar");
+ scrollbarThumb = null;
+ for (let child of SpecialPowers.InspectorUtils.getChildrenForNode(scrollbar, true, false)) {
+ if (child.nodeName === "slider") {
+ scrollbarThumb = SpecialPowers.unwrap(child.childNodes[0]);
+ }
+ }
+
+ ok(scrollbarThumb, "Should find thumb");
+ is(scrollbarThumb.nodeName, "thumb", "Should find thumb");
+
+ // Wait for finishing reflow...
+ await new Promise(resolve => SimpleTest.executeSoon(resolve));
+});
+
+add_task(function testScrollbarMouse() {
+ // Simulate mouse actions.
+ synthesizeMouseAtCenter(scrollbarThumb, {type: "mousedown", "inputSource": MouseEvent.MOZ_SOURCE_TOUCH});
+ is(scrollbar.getAttribute("curpos"), "0", "scrollbar thumb has been moved already");
+
+ synthesizeMouseAtCenter(scrollbar, {type: "mousemove", "inputSource": MouseEvent.MOZ_SOURCE_TOUCH});
+ ok(scrollbar.getAttribute("curpos") > 0, "scrollbar thumb hasn't been dragged");
+
+ synthesizeMouseAtCenter(scrollbar, {type: "mouseup", "inputSource": MouseEvent.MOZ_SOURCE_TOUCH});
+});
+
+add_task(function testScrollbarTouch() {
+ // reset scrollbar position
+ scrollbar.setAttribute("curpos", "0");
+
+ // Simulate touch actions.
+ synthesizeTouchAtCenter(scrollbarThumb, {type: "touchstart"});
+ is(scrollbar.getAttribute("curpos"), "0", "scrollbar thumb has been moved already");
+
+ synthesizeTouchAtCenter(scrollbar, {type: "touchmove"});
+ ok(scrollbar.getAttribute("curpos") > 0, "scrollbar thumb hasn't been dragged");
+
+ synthesizeTouchAtCenter(scrollbar, {type: "touchend"});
+});
+
+]]>
+</script>
+</window>
diff --git a/dom/events/test/pointerevents/test_bug1725416.html b/dom/events/test/pointerevents/test_bug1725416.html
new file mode 100644
index 0000000000..3cc4a6316f
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug1725416.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1725416</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<style>
+ #container {
+ height: 100px;
+ touch-action: none;
+ }
+</style>
+
+<div id="container"></div>
+
+<script>
+ add_task(async function testTouch() {
+ await waitUntilApzStable();
+ const container = document.getElementById("container");
+ const pointerDownPromise = promiseOneEvent(container, "pointerdown");
+ const pointerUpPromise = promiseOneEvent(container, "pointerup");
+ synthesizeNativeTapAtCenter(container);
+
+ const pointerDown = await pointerDownPromise;
+ is(pointerDown.pointerType, "touch", ".pointerType");
+ is(pointerDown.button, 0, ".button");
+ is(pointerDown.buttons, 1, ".buttons");
+
+ const pointerUp = await pointerUpPromise;
+ is(pointerUp.pointerType, "touch", ".pointerType");
+ is(pointerUp.button, 0, ".button");
+ is(pointerUp.buttons, 0, ".buttons");
+ });
+</script>
diff --git a/dom/events/test/pointerevents/test_bug968148.html b/dom/events/test/pointerevents/test_bug968148.html
new file mode 100644
index 0000000000..e62d819934
--- /dev/null
+++ b/dom/events/test/pointerevents/test_bug968148.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=968148
+-->
+<head>
+ <title>Test for Bug 968148</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 = "bug968148_inner.html";
+ }
+
+ function testWithImplicitPointerCapture() {
+ var iframe = document.getElementById("testFrame");
+ iframe.src = "bug968148_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/dom/events/test/pointerevents/test_getCoalescedEvents.html b/dom/events/test/pointerevents/test_getCoalescedEvents.html
new file mode 100644
index 0000000000..69eeac6919
--- /dev/null
+++ b/dom/events/test/pointerevents/test_getCoalescedEvents.html
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1303957
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1303957</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"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1303957">Mozilla Bug 1303957</a>
+<p id="display"></p>
+<div id="target0" style="width: 50px; height: 50px; background: green"></div>
+<script type="text/javascript">
+/** Test for Bug 1303957 **/
+SimpleTest.waitForExplicitFinish();
+
+function runTests() {
+ let target0 = window.document.getElementById("target0");
+ let utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.advanceTimeAndRefresh(0);
+
+ SimpleTest.executeSoon(async () => {
+ // Flush all pending mouse events before synthesizing events.
+
+ target0.addEventListener("pointermove", (ev) => {
+ let length = ev.getCoalescedEvents().length;
+ ok(length >= 1, "Coalesced events should >= 1, got " + length);
+
+ let rect = target0.getBoundingClientRect();
+ let prevOffsetX = 0;
+ let prevOffsetY = 0;
+
+ for (let i = 0; i < length; ++i) {
+ let coalescedEvent = ev.getCoalescedEvents()[i];
+ isnot(coalescedEvent.timeStamp, 0, "getCoalescedEvents()[" + i + "].timeStamp");
+ is(coalescedEvent.type, "pointermove", "getCoalescedEvents()[" + i + "].type");
+ is(coalescedEvent.pointerId, ev.pointerId, "getCoalescedEvents()[" + i + "].pointerId");
+ is(coalescedEvent.pointerType, ev.pointerType, "getCoalescedEvents()[" + i + "].pointerType");
+ is(coalescedEvent.isPrimary, ev.isPrimary, "getCoalescedEvents()[" + i + "].isPrimary");
+ is(coalescedEvent.target, ev.target, "getCoalescedEvents()[" + i + "].target");
+ is(coalescedEvent.currentTarget, null, "getCoalescedEvents()[" + i + "].currentTarget");
+ is(coalescedEvent.eventPhase, Event.NONE, "getCoalescedEvents()[" + i + "].eventPhase");
+ is(coalescedEvent.cancelable, false, "getCoalescedEvents()[" + i + "].cancelable");
+ is(coalescedEvent.bubbles, false, "getCoalescedEvents()[" + i + "].bubbles");
+
+ ok(coalescedEvent.offsetX >= prevOffsetX, "getCoalescedEvents()[" + i + "].offsetX = " + coalescedEvent.offsetX);
+ ok(coalescedEvent.offsetX == 5 || coalescedEvent.offsetX == 10 ||
+ coalescedEvent.offsetX == 15 || coalescedEvent.offsetX == 20, "expected offsetX");
+
+ ok(coalescedEvent.offsetY >= prevOffsetY, "getCoalescedEvents()[" + i + "].offsetY = " + coalescedEvent.offsetY);
+ ok(coalescedEvent.offsetY == 5 || coalescedEvent.offsetY == 10 ||
+ coalescedEvent.offsetY == 15 || coalescedEvent.offsetY == 20, "expected offsetY");
+
+ prevOffsetX = coalescedEvent.offsetX;
+ prevOffsetY = coalescedEvent.offsetY;
+
+ let x = rect.left + prevOffsetX;
+ let y = rect.top + prevOffsetY;
+ // coordinates may change slightly due to rounding
+ ok((coalescedEvent.clientX <= x+2) && (coalescedEvent.clientX >= x-2), "getCoalescedEvents()[" + i + "].clientX");
+ ok((coalescedEvent.clientY <= y+2) && (coalescedEvent.clientY >= y-2), "getCoalescedEvents()[" + i + "].clientY");
+ }
+ }, { once: true });
+
+ target0.addEventListener("pointerup", (ev) => {
+ utils.restoreNormalRefresh();
+ SimpleTest.finish();
+ }, { once: true });
+
+ await promiseNativeMouseEvent({ type: "mousemove", target: target0, offsetX: 5, offsetY: 5 });
+ await promiseNativeMouseEvent({ type: "mousemove", target: target0, offsetX: 10, offsetY: 10 });
+ await promiseNativeMouseEvent({ type: "mousemove", target: target0, offsetX: 15, offsetY: 15 });
+ await promiseNativeMouseEvent({ type: "mousemove", target: target0, offsetX: 20, offsetY: 20 });
+ synthesizeNativeMouseEvent({ type: "click", target: target0, offsetX: 20, offsetY: 20 });
+ });
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [["dom.events.coalesce.mousemove", true]]}, runTests);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_getCoalescedEvents_touch.html b/dom/events/test/pointerevents/test_getCoalescedEvents_touch.html
new file mode 100644
index 0000000000..0df34efb30
--- /dev/null
+++ b/dom/events/test/pointerevents/test_getCoalescedEvents_touch.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1511231
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1511231</title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.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=1303957">Mozilla Bug 1511231</a>
+<p id="display"></p>
+<div id="target0" style="margin: 50px; width: 50px; height: 50px; background: green"></div>
+<script type="text/javascript">
+/** Test for Bug 1511231 **/
+SimpleTest.waitForExplicitFinish();
+
+if (!window.opener) {
+ // the utils function in apz can't not be used in remote iframe, so run the
+ // test in a new tab.
+ info("run tests in a new tab");
+ window.open("test_getCoalescedEvents_touch.html");
+} else {
+ function runTests() {
+ let target0 = window.document.getElementById("target0");
+ let utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.advanceTimeAndRefresh(0);
+
+ SimpleTest.executeSoon(async () => {
+ // Flush all pending touch events before synthesizing events.
+
+ let eventCount = 0;
+ target0.addEventListener("pointermove", function handler(ev) {
+ eventCount++;
+ if (eventCount == 1) {
+ // Ignore the first pointermove event since the first touch event won't
+ // be coalesced.
+ is(ev.getCoalescedEvents().length, 1,
+ "Coalesced events should be 1 for the first touchmove event");
+ return;
+ }
+
+ let length = ev.getCoalescedEvents().length;
+ ok(length >= 1, "Coalesced events should >= 1, got " + length);
+
+ let rect = target0.getBoundingClientRect();
+ let prevOffsetX = 0;
+ let prevOffsetY = 0;
+
+ for (let i = 0; i < length; ++i) {
+ let coalescedEvent = ev.getCoalescedEvents()[i];
+ opener.isnot(coalescedEvent.timeStamp, 0, "getCoalescedEvents()[" + i + "].timeStamp");
+ opener.is(coalescedEvent.type, "pointermove", "getCoalescedEvents()[" + i + "].type");
+ opener.is(coalescedEvent.pointerId, ev.pointerId, "getCoalescedEvents()[" + i + "].pointerId");
+ opener.is(coalescedEvent.pointerType, ev.pointerType, "getCoalescedEvents()[" + i + "].pointerType");
+ opener.is(coalescedEvent.isPrimary, ev.isPrimary, "getCoalescedEvents()[" + i + "].isPrimary");
+ opener.is(coalescedEvent.target, ev.target, "getCoalescedEvents()[" + i + "].target");
+ opener.is(coalescedEvent.currentTarget, null, "getCoalescedEvents()[" + i + "].currentTarget");
+ opener.is(coalescedEvent.eventPhase, Event.NONE, "getCoalescedEvents()[" + i + "].eventPhase");
+ opener.is(coalescedEvent.cancelable, false, "getCoalescedEvents()[" + i + "].cancelable");
+ opener.is(coalescedEvent.bubbles, false, "getCoalescedEvents()[" + i + "].bubbles");
+
+ opener.ok(coalescedEvent.offsetX >= prevOffsetX, "getCoalescedEvents()[" + i + "].offsetX = " + coalescedEvent.offsetX);
+ opener.ok(coalescedEvent.offsetX >= 10 && coalescedEvent.offsetX <= 40, "expected offsetX");
+
+ opener.ok(coalescedEvent.offsetY >= prevOffsetY, "getCoalescedEvents()[" + i + "].offsetY = " + coalescedEvent.offsetY);
+ opener.ok(coalescedEvent.offsetY >= 10 && coalescedEvent.offsetY <= 40, "expected offsetY");
+
+ prevOffsetX = coalescedEvent.offsetX;
+ prevOffsetY = coalescedEvent.offsetY;
+
+ let x = rect.left + prevOffsetX;
+ let y = rect.top + prevOffsetY;
+ // coordinates may change slightly due to rounding
+ opener.ok((coalescedEvent.clientX <= x+2) && (coalescedEvent.clientX >= x-2), "getCoalescedEvents()[" + i + "].clientX");
+ opener.ok((coalescedEvent.clientY <= y+2) && (coalescedEvent.clientY >= y-2), "getCoalescedEvents()[" + i + "].clientY");
+ }
+
+ target0.removeEventListener("pointermove", handler);
+ });
+
+ target0.addEventListener("pointerup", (ev) => {
+ utils.restoreNormalRefresh();
+ opener.SimpleTest.finish();
+ window.close();
+ }, { once: true });
+
+ let positions = [];
+ for (let i = 10; i <= 40; i+=5) {
+ positions.push([{ x: i, y: i }]);
+ }
+
+ await synthesizeNativeTouchSequences(target0, positions);
+ });
+ }
+
+ SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["dom.events.coalesce.touchmove", true],
+ ["dom.events.compress.touchmove", false],
+ ]}, runTests);
+ });
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_multiple_touches.html b/dom/events/test/pointerevents/test_multiple_touches.html
new file mode 100644
index 0000000000..15202c3636
--- /dev/null
+++ b/dom/events/test/pointerevents/test_multiple_touches.html
@@ -0,0 +1,196 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for Multiple Touches</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>
+<p id="display"></p>
+<div id="target0" style="width: 100px; height: 100px; background: green"></div>
+<div id="target1" style="width: 100px; height: 100px; background: red"></div>
+<script type="text/javascript">
+// TODO: We should probably make EventUtils.js to support multiple touch.
+// Currently the use case is simple, so we just add support here.
+// Once we have more use cases, we could come out a more generic way to
+// support it.
+var touches = {
+ ids: [],
+ lefts: [],
+ tops: [],
+ rxs: [],
+ rys: [],
+ angles: [],
+ forces: [],
+ tiltXs: [],
+ tiltYs: [],
+ twists: [],
+};
+
+function synthesizeTouchEvent(aType, aTouches) {
+ var utils = _getDOMWindowUtils(window);
+ if (!utils) {
+ ok(false, "unable to get nsIDOMWindowUtils");
+ return;
+ }
+
+ utils.sendTouchEvent(aType, aTouches.ids, aTouches.lefts, aTouches.tops,
+ aTouches.rxs, aTouches.rys, aTouches.angles,
+ aTouches.forces, aTouches.tiltXs, aTouches.tiltYs,
+ aTouches.twists, 0 /* modifiers */);
+}
+
+function synthesizeTouchStart(aTarget, aId, aOffsetX, aOffsetY) {
+ if (touches.ids.some((aElem) => { return aElem === aId; })) {
+ ok(false, `touch with id=${aTouch.id} is already registered`);
+ return;
+ }
+
+ let rect = aTarget.getBoundingClientRect();
+ touches.ids.push(aId);
+ touches.lefts.push(rect.left + aOffsetX);
+ touches.tops.push(rect.top + aOffsetY);
+ touches.rxs.push(1);
+ touches.rys.push(1);
+ touches.angles.push(0);
+ touches.forces.push(1);
+ touches.tiltXs.push(0);
+ touches.tiltYs.push(0);
+ touches.twists.push(0);
+
+ synthesizeTouchEvent("touchstart", touches);
+}
+
+function synthesizeTouchEnd(aTarget, aId, aOffsetX, aOffsetY) {
+ let index = touches.ids.indexOf(aId);
+ if (-1 === index) {
+ ok(false, `touch with id=${aTouch.id} isn't registered`);
+ return;
+ }
+
+ var removedTouches = {
+ ids: touches.ids.splice(index, 1),
+ lefts: touches.lefts.splice(index, 1),
+ tops: touches.tops.splice(index, 1),
+ rxs: touches.rxs.splice(index, 1),
+ rys: touches.rys.splice(index, 1),
+ angles: touches.angles.splice(index, 1),
+ forces: touches.forces.splice(index, 1),
+ tiltXs: touches.tiltXs.splice(index, 1),
+ tiltYs: touches.tiltYs.splice(index, 1),
+ twists: touches.twists.splice(index, 1),
+ };
+
+ synthesizeTouchEvent("touchend", removedTouches);
+}
+
+function synthesizeTouchMove(aTarget, aId, aOffsetX, aOffsetY) {
+ let index = touches.ids.indexOf(aId);
+ if (-1 === index) {
+ ok(false, `touch with id=${aTouch.id} isn't registered`);
+ return;
+ }
+
+ let rect = aTarget.getBoundingClientRect();
+ touches.lefts[index] = rect.left + aOffsetX;
+ touches.tops[index] = rect.top + aOffsetY;
+
+ synthesizeTouchEvent("touchmove", touches);
+}
+
+var target0 = document.getElementById("target0");
+var target1 = document.getElementById("target1");
+
+function WaitExpectedEvents(aListenEvents, aExpectedEvents, aEventGenerator) {
+ let promise = new Promise(function(aResolve) {
+ let index = 0;
+ let checkReceivedEvents = function(aEvent) {
+ if (aExpectedEvents.length === 0) {
+ ok(false, `receive unexpected ${aEvent.type} event from ${aEvent.target}`);
+ return;
+ }
+ index++;
+ let expectedResult = aExpectedEvents.shift();
+ isDeeply(expectedResult, [aEvent.target, aEvent.type], `${index}. expect receive ${expectedResult[1]} event from ${expectedResult[0]}`);
+ if (aExpectedEvents.length === 0) {
+ // Wait a bit to see if there is any additional unexpected event fired.
+ setTimeout(function() {
+ // Clean up
+ aListenEvents.forEach((aElem) => {
+ target0.removeEventListener(aElem, checkReceivedEvents);
+ target1.removeEventListener(aElem, checkReceivedEvents);
+ });
+ aResolve();
+ }, 0);
+ }
+ };
+
+ aListenEvents.forEach((aElem) => {
+ target0.addEventListener(aElem, checkReceivedEvents);
+ target1.addEventListener(aElem, checkReceivedEvents);
+ });
+ });
+
+ aEventGenerator();
+
+ return promise;
+}
+
+// eslint-disable-next-line mozilla/no-addtask-setup
+add_task(async function setup() {
+ await SimpleTest.promiseFocus();
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.w3c_pointer_events.implicit_capture", false]]
+ });
+});
+
+// Test for bug 1521082
+add_task(async function ShouldNotSendDuplicatedPointerDown() {
+ return WaitExpectedEvents(
+ ["pointerup", "pointerdown"],
+ [ // [event target, event type]
+ [target0, "pointerdown"],
+ [target1, "pointerdown"],
+ [target1, "pointerup"],
+ [target0, "pointerup"],
+ ],
+ function() {
+ var defaultId = SpecialPowers.Ci.nsIDOMWindowUtils.DEFAULT_TOUCH_POINTER_ID;
+ synthesizeTouchStart(target0, defaultId, 10, 10);
+ synthesizeTouchStart(target1, defaultId + 1, 10, 10);
+ synthesizeTouchEnd(target1, defaultId + 1, 10, 10);
+ synthesizeTouchEnd(target0, defaultId, 10, 10);
+ }
+ );
+});
+
+// Test for bug 1323400
+add_task(async function ShouldNotSendDuplicatedPointerMove() {
+ return WaitExpectedEvents(
+ ["pointerup", "pointerdown","pointermove"],
+ [ // [event target, event type]
+ [target0, "pointerdown"],
+ [target1, "pointerdown"],
+ [target1, "pointermove"],
+ // Should receive only one pointer event for target 1.
+ [target1, "pointermove"],
+ [target1, "pointerup"],
+ [target0, "pointerup"],
+ ],
+ function() {
+ var defaultId = SpecialPowers.Ci.nsIDOMWindowUtils.DEFAULT_TOUCH_POINTER_ID;
+ synthesizeTouchStart(target0, defaultId, 10, 10);
+ synthesizeTouchStart(target1, defaultId + 1, 10, 10);
+ synthesizeTouchMove(target1, defaultId + 1, 11, 11);
+ synthesizeTouchMove(target1, defaultId + 1, 12, 12);
+ synthesizeTouchEnd(target1, defaultId + 1, 10, 10);
+ synthesizeTouchEnd(target0, defaultId, 10, 10);
+ }
+ );
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_pointercapture_remove_iframe.html b/dom/events/test/pointerevents/test_pointercapture_remove_iframe.html
new file mode 100644
index 0000000000..fca3a440bf
--- /dev/null
+++ b/dom/events/test/pointerevents/test_pointercapture_remove_iframe.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1686037
+-->
+<head>
+<title>Bug 1686037</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>
+#target {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+}
+iframe {
+ width: 400px;
+ height: 300px;
+ border: 1px solid blue;
+}
+</style>
+</head>
+<body>
+<a target="_blank"href="https://bugzilla.mozilla.org/show_bug.cgi?id=1686037">Mozilla Bug 1686037</a>
+<div id="target"></div>
+<iframe srcdoc="<div style='width: 100px; height: 100px; background-color: blue;'></div>"></iframe>
+
+<pre id="test">
+<script type="text/javascript">
+/**
+ * Test for Bug 1686037
+ */
+function waitForEvent(aTarget, aEventName, aCallback = null) {
+ return new Promise((aResolve) => {
+ aTarget.addEventListener(aEventName, async (e) => {
+ ok(true, `got ${e.type} event on ${e.target}, pointerid: ${e.pointerId}`);
+ if (aCallback) {
+ await aCallback(e);
+ }
+ aResolve();
+ }, { once: true });
+ });
+}
+
+function waitForPointerDownAndSetPointerCapture(aTarget) {
+ return waitForEvent(aTarget, "pointerdown", async (event) => {
+ return new Promise((aResolve) => {
+ aTarget.addEventListener("gotpointercapture", (e) => {
+ ok(true, `got ${e.type} event on ${e.target}, pointerid: ${e.pointerId}`);
+ aResolve();
+ }, { once: true });
+
+ aTarget.setPointerCapture(event.pointerId);
+ });
+ });
+}
+
+add_task(async function test_remove_iframe_after_pointer_capture() {
+ await SimpleTest.promiseFocus();
+
+ let iframe = document.querySelector("iframe");
+ let iframeWin = iframe.contentWindow;
+ let targetInIframe = iframe.contentDocument.querySelector("div");
+ let promise = Promise.all([
+ waitForPointerDownAndSetPointerCapture(targetInIframe),
+ waitForEvent(targetInIframe, "pointermove")
+ ]);
+ synthesizeTouch(targetInIframe, 10, 10, { type: "touchstart", id: 10 }, iframeWin);
+ synthesizeTouch(targetInIframe, 11, 11, { type: "touchmove", id: 10 }, iframeWin);
+ await promise;
+
+ // Intentionally not synthesize touchend event to not trigger implicit releasing
+ // pointer capture. And iframe removal should trigger pointer capture clean up.
+ iframe.remove();
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_pointercapture_xorigin_iframe.html b/dom/events/test/pointerevents/test_pointercapture_xorigin_iframe.html
new file mode 100644
index 0000000000..2bfb0e8ac1
--- /dev/null
+++ b/dom/events/test/pointerevents/test_pointercapture_xorigin_iframe.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<title>Test for pointer capture</title>
+<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="#">Test for pointer capture</a>
+<div id="content"></div>
+<pre id="test">
+<script type="application/javascript">
+/**
+ * Pointer capture tests.
+ **/
+
+SimpleTest.waitForExplicitFinish();
+
+let gTestFiles = [
+ "file_pointercapture_xorigin_iframe.html",
+ "file_pointercapture_xorigin_iframe_pointerlock.html",
+ "file_pointercapture_xorigin_iframe_touch.html",
+];
+
+let gTestWindow = null;
+let gTestIndex = 0;
+
+SpecialPowers.pushPrefEnv({"set": [
+ // This will make dispatched event going through parent process.
+ ["test.events.async.enabled", true]
+]}, nextTest);
+
+function nextTest() {
+ if (gTestWindow) {
+ gTestWindow.close();
+ }
+ SimpleTest.waitForFocus(runNextTest);
+}
+
+function runNextTest() {
+ if (gTestIndex < gTestFiles.length) {
+ let file = gTestFiles[gTestIndex];
+ gTestIndex++;
+
+ info(`Testing ${file}`);
+ gTestWindow = window.open(file, "", "width=500,height=500");
+ } else {
+ SimpleTest.finish();
+ }
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_pointermove_drag_scrollbar.html b/dom/events/test/pointerevents/test_pointermove_drag_scrollbar.html
new file mode 100644
index 0000000000..27ef2829ef
--- /dev/null
+++ b/dom/events/test/pointerevents/test_pointermove_drag_scrollbar.html
@@ -0,0 +1,77 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1509710
+-->
+<head>
+<meta charset="utf-8">
+<title>Test for Bug 1509710</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>
+#scroll {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+}
+#scrolled {
+ width: 200px;
+ height: 1000px; /* so the subframe has room to scroll */
+ will-change: transform; /* to force active layers */
+}
+</style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1509710">Mozilla Bug 1509710</a>
+<p id="display"></p>
+<div id="scroll">
+<div id="scrolled"></div>
+</div>
+<script type="text/javascript">
+
+/** Test for Bug 1509710 **/
+add_task(async function test_pointer_mouse_event() {
+ await SimpleTest.promiseFocus();
+
+ let subframe = document.getElementById("scroll");
+ if (subframe.clientWidth == 200) {
+ // No scrollbar, abort the test. This can happen e.g. on local macOS runs
+ // with OS settings to only show scrollbars on trackpad/mouse activity.
+ ok(false, "No scrollbars found, cannot run this test!");
+ return;
+ }
+
+ let receivedEvent = {};
+ let handler = function(e) {
+ receivedEvent[e.type] = true;
+ };
+ subframe.addEventListener("pointerdown", handler);
+ subframe.addEventListener("pointermove", handler);
+ subframe.addEventListener("pointerup", handler);
+ subframe.addEventListener("mousedown", handler);
+ subframe.addEventListener("mousemove", handler);
+ subframe.addEventListener("mouseup", handler);
+
+ // synthesize mouse actions on scrollbar.
+ let scrollbarX = (200 + subframe.clientWidth) / 2;
+ synthesizeMouse(subframe, scrollbarX, 30, { type: "mousedown" });
+ synthesizeMouse(subframe, scrollbarX, 40, { type: "mousemove" });
+ synthesizeMouse(subframe, scrollbarX, 40, { type: "mouseup" });
+
+ await new Promise(SimpleTest.executeSoon);
+
+ // Test pointer event
+ ok(receivedEvent.pointerdown, "should receive pointerdown event");
+ ok(!receivedEvent.pointermove, "should not receive pointermove event");
+ ok(receivedEvent.pointerup, "should receive pointerup event");
+
+ // Test mouse event
+ ok(receivedEvent.mousedown, "should receive mousedown event");
+ ok(!receivedEvent.mousemove, "should not receive mousemove event");
+ ok(receivedEvent.mouseup, "should receive mouseup event");
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
new file mode 100644
index 0000000000..790f2cdd55
--- /dev/null
+++ b/dom/events/test/pointerevents/test_remove_frame_when_got_pointer_capture.html
@@ -0,0 +1,166 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for triggering popup by pointer 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>
+<p id="content">
+</p>
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+function startTest() {
+ let content = document.getElementById('content');
+ let iframe = document.createElement('iframe');
+ iframe.width = 200;
+ iframe.height = 200;
+ content.appendChild(iframe);
+ iframe.contentDocument.body.innerHTML =
+ "<div id='div1' style='width: 50px; height: 50px; background: green'></div>" +
+ "<div id='div2' style='width: 50px; height: 50px; background: red'></div>";
+
+ let div1 = iframe.contentDocument.getElementById("div1");
+ let div2 = iframe.contentDocument.getElementById("div2");
+ let divEvents = [
+ "pointerdown",
+ "gotpointercapture",
+ "pointermove",
+ "pointerup",
+ "lostpointercapture",
+ "mousedown",
+ "mousemove",
+ "mouseup",
+ ];
+
+ let documentEvents = [
+ "pointerdown",
+ "pointermove",
+ "pointerup",
+ "mousedown",
+ "mousemove",
+ "mouseup",
+ ];
+
+ divEvents.forEach((event) => {
+ div1.addEventListener(event, (e) => {
+ ok(divEvents.includes(e.type), " don't expect " + e.type);
+ divEvents = divEvents.filter(item => item !== e.type);
+ }, { once: true });
+ });
+
+ documentEvents.forEach((event) => {
+ iframe.contentDocument.addEventListener(event, (e) => {
+ is(e.target, div1, e.type + " should be dispatched to div1");
+ }, { once: true });
+ });
+
+ div1.addEventListener("pointerdown", (e) => {
+ div1.setPointerCapture(e.pointerId);
+ });
+
+ div1.addEventListener("gotpointercapture", (e) => {
+ div1.style.display = "none";
+ });
+
+ info("Tests for mouseup");
+ synthesizeMouseAtCenter(div1, {type: "mousedown"}, iframe.contentWindow);
+ synthesizeMouseAtCenter(div2, {type: "mousemove"}, iframe.contentWindow);
+ synthesizeMouseAtCenter(div2, {type: "mouseup"}, iframe.contentWindow);
+
+ ok(!divEvents.length, " expect " + divEvents);
+
+ divEvents = [
+ "pointerdown",
+ "gotpointercapture",
+ "pointermove",
+ "pointerup",
+ "lostpointercapture",
+ "touchstart",
+ "touchmove",
+ "touchend",
+ ];
+
+ documentEvents = [
+ "pointerdown",
+ "pointermove",
+ "pointerup",
+ "touchstart",
+ "touchmove",
+ "touchend",
+ ];
+ divEvents.forEach((event) => {
+ div1.addEventListener(event, (e) => {
+ ok(divEvents.includes(e.type), " don't expect " + e.type);
+ divEvents = divEvents.filter(item => item !== e.type);
+ }, { once: true });
+ });
+
+ documentEvents.forEach((event) => {
+ iframe.contentDocument.addEventListener(event, (e) => {
+ is(e.target, div1, e.type + " should be dispatched to div1");
+ }, { once: true });
+ });
+
+ info("Tests for touchend");
+ div1.style.display = "block";
+ synthesizeMouseAtCenter(div1, {type: "mousemove"}, iframe.contentWindow);
+ synthesizeTouch(div1, 5, 5, { type: "touchstart" }, iframe.contentWindow);
+ synthesizeTouch(div2, 5, 5, { type: "touchmove" }, iframe.contentWindow);
+ synthesizeTouch(div2, 5, 5, { type: "touchend" }, iframe.contentWindow);
+
+ ok(!divEvents.length, " expect " + divEvents);
+
+ divEvents = [
+ "pointerdown",
+ "gotpointercapture",
+ "pointermove",
+ "pointercancel",
+ "lostpointercapture",
+ "touchstart",
+ "touchmove",
+ "touchcancel",
+ ];
+
+ documentEvents = [
+ "pointerdown",
+ "pointermove",
+ "pointercancel",
+ "touchstart",
+ "touchmove",
+ "touchcancel",
+ ];
+ divEvents.forEach((event) => {
+ div1.addEventListener(event, (e) => {
+ ok(divEvents.includes(e.type), " don't expect " + e.type);
+ divEvents = divEvents.filter(item => item !== e.type);
+ }, { once: true });
+ });
+
+ documentEvents.forEach((event) => {
+ iframe.contentDocument.addEventListener(event, (e) => {
+ is(e.target, div1, e.type + " should be dispatched to div1");
+ }, { once: true });
+ });
+
+ info("Tests for touchcancel");
+ div1.style.display = "block";
+ synthesizeMouseAtCenter(div1, {type: "mousemove"}, iframe.contentWindow);
+ synthesizeTouch(div1, 5, 5, { type: "touchstart" }, iframe.contentWindow);
+ synthesizeTouch(div2, 5, 5, { type: "touchmove" }, iframe.contentWindow);
+ synthesizeTouch(div2, 5, 5, { type: "touchcancel" }, iframe.contentWindow);
+
+ ok(!divEvents.length, " expect " + divEvents);
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(startTest);
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_synthesized_touch.html b/dom/events/test/pointerevents/test_synthesized_touch.html
new file mode 100644
index 0000000000..8a79303e74
--- /dev/null
+++ b/dom/events/test/pointerevents/test_synthesized_touch.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test synthesized touch input</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<style>
+ #container {
+ width: 100px;
+ height: 100px;
+ background-color: green;
+ }
+</style>
+
+<div id="container"></div>
+
+<script>
+function waitForEvent(aTarget, aEvent, aCheckFn) {
+ return new Promise(aResolve => {
+ aTarget.addEventListener(aEvent, function(e) {
+ info(`${aEvent} received`);
+ aCheckFn(e);
+ aResolve();
+ }, { once: true });
+ });
+}
+
+add_task(async function test() {
+ const tiltX = 10;
+ const tiltY = -10;
+ const twist = 5;
+ const check = function(aEvent) {
+ is(aEvent.tiltX, tiltX, "check tiltX");
+ is(aEvent.tiltY, tiltY, "check tiltY");
+ is(aEvent.twist, twist, "check twist");
+ };
+
+ const container = document.getElementById("container");
+ const pointerDownPromise = waitForEvent(container, "pointerdown", check);
+ const pointerUpPromise = waitForEvent(container, "pointerup", check);
+
+ synthesizeTouchAtCenter(container, {tiltX, tiltY, twist});
+
+ await Promise.all([pointerDownPromise, pointerUpPromise]);
+});
+</script>
diff --git a/dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html b/dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html
new file mode 100644
index 0000000000..abdf92831a
--- /dev/null
+++ b/dom/events/test/pointerevents/test_trigger_fullscreen_by_pointer_events.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for triggering Fullscreen by pointer 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>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+function startTest() {
+ let win = window.open("file_test_trigger_fullscreen.html", "_blank");
+ win.addEventListener("load", () => {
+ let target = win.document.getElementById("target");
+ target.addEventListener("pointerdown", () => {
+ target.requestFullscreen();
+ target.addEventListener("pointerdown", () => {
+ win.document.exitFullscreen();
+ }, {once: true});
+ }, {once: true});
+
+ win.document.addEventListener("fullscreenchange", () => {
+ if (win.document.fullscreenElement) {
+ is(win.document.fullscreenElement, target, "fullscreenElement should be the div element");
+ // synthesize mouse events to generate pointer events and leave full screen.
+ synthesizeMouseAtCenter(target, { type: "mousedown" }, win);
+ synthesizeMouseAtCenter(target, { type: "mouseup" }, win);
+ } else {
+ win.close();
+ SimpleTest.finish();
+ }
+ });
+ // Make sure our window is focused before starting the test
+ SimpleTest.waitForFocus(() => {
+ // synthesize mouse events to generate pointer events and enter full screen.
+ synthesizeMouseAtCenter(target, { type: "mousedown" }, win);
+ synthesizeMouseAtCenter(target, { type: "mouseup" }, win);
+ }, win);
+ });
+}
+
+SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["full-screen-api.allow-trusted-requests-only", false]
+ ]
+ }, startTest);
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_attributes_hoverable_pointers-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_attributes_hoverable_pointers-manual.html
new file mode 100644
index 0000000000..557ee80c4f
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_attributes_hoverable_pointers-manual.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test pointerevent attributes for hoverable pointers</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_attributes_hoverable_pointers-manual.html");
+ }
+ function executeTest(int_win) {
+ let iframeWin = int_win.document.getElementById("innerFrame").contentWindow;
+ // synthesize mouse events with input source = mouse
+ sendMouseEvent(int_win, "square1", "mousemove", {button:-1});
+ sendMouseEvent(int_win, "square1", "mousedown");
+ sendMouseEvent(int_win, "square1", "mouseup");
+ sendMouseEvent(int_win, "square1", "mousemove", {button:-1,
+ offsetX:-1,
+ offsetY:-1});
+ sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1});
+ sendMouseEvent(iframeWin, "square2", "mousedown");
+ sendMouseEvent(iframeWin, "square2", "mouseup");
+ sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1,
+ offsetX:-1,
+ offsetY:-1});
+ // synthesize mouse events with input source = pen
+ let inputPen = MouseEvent.MOZ_SOURCE_PEN;
+ sendMouseEvent(int_win, "square1", "mousemove", {button:-1,
+ inputSource: inputPen});
+ sendMouseEvent(int_win, "square1", "mousedown", {inputSource:inputPen});
+ sendMouseEvent(int_win, "square1", "mouseup", {inputSource:inputPen});
+ sendMouseEvent(int_win, "square1", "mousemove", {button:-1,
+ offsetX:-1,
+ offsetY:-1,
+ inputSource:inputPen});
+ sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1,
+ inputSource:inputPen});
+ sendMouseEvent(iframeWin, "square2", "mousedown", {inputSource:inputPen});
+ sendMouseEvent(iframeWin, "square2", "mouseup", {inputSource:inputPen});
+ sendMouseEvent(iframeWin, "square2", "mousemove", {button:-1,
+ offsetX:-1,
+ offsetY:-1,
+ inputSource:inputPen});
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_attributes_nohover_pointers-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_attributes_nohover_pointers-manual.html
new file mode 100644
index 0000000000..f9fde4f92f
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_attributes_nohover_pointers-manual.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>Test pointerevent attributes for non-hoverable pointers</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_attributes_nohover_pointers-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "square1", "touchstart");
+ sendTouchEvent(int_win, "square1", "touchend");
+ let iframe = int_win.document.getElementById("innerFrame");
+ sendTouchEvent(iframe.contentWindow, "square2", "touchstart");
+ sendTouchEvent(iframe.contentWindow, "square2", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_boundary_events_in_capturing-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_boundary_events_in_capturing-manual.html
new file mode 100644
index 0000000000..24aeb6d9a6
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_boundary_events_in_capturing-manual.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>W3C pointerevent_boundary_events_in_capturing-manual.html in Mochitest form</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_boundary_events_in_capturing-manual.html", true);
+ }
+ function executeTest(int_win) {
+ sendMouseEvent(int_win, "target0", "mousemove");
+ sendMouseEvent(int_win, "target0", "mousedown");
+ sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
+ sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
+ sendMouseEvent(int_win, "target0", "mouseup");
+
+ window.addEventListener("message", function(aEvent) {
+ if (aEvent.data == "Test Touch") {
+ // Synthesize touch events to run this test.
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 10});
+ sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 15});
+ sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 20});
+ sendTouchEvent(int_win, "target0", "touchend");
+ window.postMessage("Test Pen", "*");
+ } else if (aEvent.data == "Test Pen") {
+ // Synthesize pen events to run this test.
+ sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
+ sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
+ sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ }
+ });
+ window.postMessage("Test Touch", "*");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html
new file mode 100644
index 0000000000..f95b16c850
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_change-touch-action-onpointerdown_touch-manual.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("pointerevent_change-touch-action-onpointerdown_touch-manual.html");
+ }
+ function executeTest(int_win) {
+ const WM_VSCROLL = 0x0115;
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchmove");
+ sendTouchEvent(int_win, "target0", "touchend");
+
+ // NOTE: This testcase is about that modifying touch-action during a
+ // pointerdown callback "should not" affect the gesture detection of the
+ // touch session started by the pointerdown. That is, a scroll should
+ // still fired by gesture detection, instead of launching by our own.
+ var utils = _getDOMWindowUtils(int_win);
+ var target0 = int_win.document.getElementById("target0");
+ utils.sendNativeMouseScrollEvent(target0.getBoundingClientRect().left + 5,
+ target0.getBoundingClientRect().top + 5,
+ WM_VSCROLL, 10, 10, 0, 0, 0, target0);
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html b/dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html
new file mode 100644
index 0000000000..058e32a967
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_constructor.html
@@ -0,0 +1,26 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_constructor.html");
+ }
+ function executeTest(int_win) {
+ // Function should be, but can be empty
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_drag_interaction-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_drag_interaction-manual.html
new file mode 100644
index 0000000000..a05ee9557a
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_drag_interaction-manual.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1669673
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1669673</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/html/pointerevent_drag_interaction-manual.html");
+ }
+ async function executeTest(int_win) {
+ info("executeTest");
+ // DndWithoutCapture
+ await doDragAndDrop(int_win, "target0", "target1");
+ // DndWithCapture
+ await doDragAndDrop(int_win, "target0", "target1");
+ // DndWithCaptureMouse
+ await doDragAndDrop(int_win, "target0", "target1");
+ // DndPrevented
+ await doDragAndDrop(int_win, "target0", "target1", {
+ expectCancelDragStart: true,
+ // Move mouse to target1.
+ stepY: 33
+ });
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_movementxy-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_movementxy-manual.html
new file mode 100644
index 0000000000..3059f868b7
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_movementxy-manual.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1399740
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1399740</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerlock/pointerevent_movementxy-manual.html");
+ }
+ function executeTest(int_win) {
+ let box1 = int_win.document.getElementById("box1");
+ let box2 = int_win.document.getElementById("box2");
+ let rect1 = box1.getBoundingClientRect();
+ let rect2 = box2.getBoundingClientRect();
+ let offsetX = rect1.left + rect1.width / 2;
+ let offsetY = rect1.top + rect1.height / 2;
+ let stepX = (rect2.left + rect2.width / 2 - offsetX) / 10;
+ let stepY = (rect2.top + rect2.height / 2 - offsetY) / 10;
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_MOUSE});
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_MOUSE});
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_MOUSE});
+ for (var i = 0; i < 10; ++i) {
+ offsetX += stepX;
+ offsetY += stepY;
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_MOUSE});
+ }
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_MOUSE});
+
+ offsetX = rect1.left + rect1.width / 2;
+ offsetY = rect1.top + rect1.height / 2;
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_TOUCH});
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_TOUCH});
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_TOUCH});
+ for (var i = 0; i < 10; ++i) {
+ offsetX += stepX;
+ offsetY += stepY;
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_TOUCH});
+ }
+ sendMouseEventAtPoint(int_win, offsetX, offsetY, "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_TOUCH});
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_multiple_primary_pointers_boundary_events-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_multiple_primary_pointers_boundary_events-manual.html
new file mode 100644
index 0000000000..825e23857f
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_multiple_primary_pointers_boundary_events-manual.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html");
+ }
+ function executeTest(int_win) {
+ sendMouseEvent(int_win, "target0", "mousemove");
+ sendTouchEvent(int_win, "target1", "touchstart");
+ sendTouchEvent(int_win, "target1", "touchend");
+ sendMouseEvent(int_win, "target0", "mousemove");
+ sendMouseEvent(int_win, "done", "mousedown");
+ sendMouseEvent(int_win, "done", "mouseup");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html
new file mode 100644
index 0000000000..f52bf7fc20
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerId_scope-manual.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_pointerId_scope-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointercancel_touch-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointercancel_touch-manual.html
new file mode 100644
index 0000000000..0adba4f756
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_pointercancel_touch-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_pointercancel_touch-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchcancel");
+
+ // Need a touchend event to terminated the test gracefully.
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_after_pointercancel_touch-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_after_pointercancel_touch-manual.html
new file mode 100644
index 0000000000..53c897bcd0
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_after_pointercancel_touch-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_pointerleave_after_pointercancel_touch-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchcancel");
+
+ // Need a touchend event to terminated the test gracefully.
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_pen-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_pen-manual.html
new file mode 100644
index 0000000000..e4904780f6
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerleave_pen-manual.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_pointerleave_pen-manual.html");
+ }
+ function executeTest(int_win) {
+ sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_after_pointercancel_touch-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_after_pointercancel_touch-manual.html
new file mode 100644
index 0000000000..53cf765fb6
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_after_pointercancel_touch-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_pointerout_after_pointercancel_touch-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchcancel");
+
+ // Need a touchend event to terminated the test gracefully.
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_pen-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_pen-manual.html
new file mode 100644
index 0000000000..6b41f6492b
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_pointerout_pen-manual.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_pointerout_pen-manual.html");
+ }
+ function executeTest(int_win) {
+ sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mousecancel", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_events_to_original_target-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_events_to_original_target-manual.html
new file mode 100644
index 0000000000..049c41467f
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_events_to_original_target-manual.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html");
+ }
+ function executeTest(int_win) {
+ // Synthesize mouse events to run this test.
+ sendMouseEvent(int_win, "target0", "mousemove");
+ sendMouseEvent(int_win, "target0", "mousedown");
+ sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
+ sendMouseEvent(int_win, "target0", "mousemove", {buttons: 1});
+ sendMouseEvent(int_win, "target0", "mouseup");
+
+ window.addEventListener("message", function(aEvent) {
+ if (aEvent.data == "Test Touch") {
+ // Synthesize touch events to run this test.
+ sendTouchEvent(int_win, "target0", "touchstart", {offsetX: 10});
+ sendTouchEvent(int_win, "target0", "touchmove", {offsetX: 11});
+ sendTouchEvent(int_win, "target0", "touchend", {offsetX: 11});
+ window.postMessage("Test Pen", "*");
+ } else if (aEvent.data == "Test Pen") {
+ // Synthesize pen events to run this test.
+ sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mousedown", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
+ sendMouseEvent(int_win, "target0", "mousemove", {inputSource:MouseEvent.MOZ_SOURCE_PEN, buttons: 1});
+ sendMouseEvent(int_win, "target0", "mouseup", {inputSource:MouseEvent.MOZ_SOURCE_PEN});
+ }
+ });
+ window.postMessage("Test Touch", "*");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_onpointercancel_touch-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_onpointercancel_touch-manual.html
new file mode 100644
index 0000000000..eb39b0bee3
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_releasepointercapture_onpointercancel_touch-manual.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1000870
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1000870</title>
+ <meta name="author" content="Maksim Lebedev" />
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target0", "touchstart");
+ sendTouchEvent(int_win, "target0", "touchcancel");
+
+ // Need a touchend event to terminated the test gracefully.
+ sendTouchEvent(int_win, "target0", "touchend");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_pointerevent_sequence_at_implicit_release_on_drag-manual.html b/dom/events/test/pointerevents/test_wpt_pointerevent_sequence_at_implicit_release_on_drag-manual.html
new file mode 100644
index 0000000000..40a6be2e71
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_pointerevent_sequence_at_implicit_release_on_drag-manual.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>W3C pointerevent_sequence_at_implicit_release_on_drag-manual.html in Mochitest form</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script type="text/javascript" src="mochitest_support_external.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="text/javascript">
+ SimpleTest.waitForExplicitFinish();
+ function startTest() {
+ runTestInNewWindow("wpt/pointerevent_sequence_at_implicit_release_on_drag-manual.html");
+ }
+ function executeTest(int_win) {
+ sendTouchEvent(int_win, "target", "touchstart");
+ sendTouchEvent(int_win, "target", "touchmove");
+ sendTouchEvent(int_win, "target", "touchmove");
+ sendTouchEvent(int_win, "target", "touchcancel");
+ sendMouseEvent(int_win, "done", "mousedown");
+ sendMouseEvent(int_win, "done", "mouseup");
+ }
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/test_wpt_touch_action.html b/dom/events/test/pointerevents/test_wpt_touch_action.html
new file mode 100644
index 0000000000..f2edbfb061
--- /dev/null
+++ b/dom/events/test/pointerevents/test_wpt_touch_action.html
@@ -0,0 +1,98 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>W3C pointerevents/*touch-action*.html tests in Mochitest form</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="apz_test_utils.js"></script>
+ <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+var apz_touch_action_prefs = [
+ // Dropping the touch slop to 0 makes the tests easier to write because
+ // we can just do a one-pixel drag to get over the pan threshold rather
+ // than having to hard-code some larger value.
+ ["apz.touch_start_tolerance", "0.0"],
+ // The touchstart from the drag can turn into a long-tap if the touch-move
+ // events get held up. Try to prevent that by making long-taps require
+ // a 10 second hold. Note that we also cannot enable chaos mode on this
+ // test for this reason, since chaos mode can cause the long-press timer
+ // to fire sooner than the pref dictates.
+ ["ui.click_hold_context_menus.delay", 10000],
+ // The subtests in this test do touch-drags to pan the page, but we don't
+ // want those pans to turn into fling animations, so we increase the
+ // fling-stop threshold velocity to absurdly high.
+ ["apz.fling_stopped_threshold", Number.MAX_VALUE],
+ // The helper_div_pan's div gets a displayport on scroll, but if the
+ // test takes too long the displayport can expire before the new scroll
+ // position is synced back to the main thread. So we disable displayport
+ // expiry for these tests.
+ ["apz.displayport_expiry_ms", 0],
+];
+
+function apzScriptInjector(name) {
+ return async function(childWin) {
+ childWin._ACTIVE_TEST_NAME = name;
+ await injectScript('/tests/SimpleTest/paint_listener.js', childWin);
+ await injectScript('../apz_test_utils.js', childWin);
+ await injectScript('../apz_test_native_event_utils.js', childWin);
+ await injectScript('../touch_action_helpers.js', childWin);
+ };
+}
+
+// Each of these test names is turned into an entry in the |subtests| array
+// below.
+var testnames = [
+ 'pointerevent_touch-action-auto-css_touch-manual',
+ 'pointerevent_touch-action-button-test_touch-manual',
+ // this one runs as a web-platform-test since it's not a manual test
+ // 'pointerevent_touch-action-illegal',
+ 'pointerevent_touch-action-inherit_child-auto-child-none_touch-manual',
+ 'pointerevent_touch-action-inherit_child-none_touch-manual',
+ 'pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual',
+ 'pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual',
+ 'pointerevent_touch-action-inherit_highest-parent-none_touch-manual',
+ 'pointerevent_touch-action-inherit_parent-none_touch-manual',
+ // the keyboard-manual and mouse-manual tests require simulating keyboard/
+ // mouse input, rather than touch, so we're not going to do that here.
+ //'pointerevent_touch-action-keyboard-manual',
+ //'pointerevent_touch-action-mouse-manual',
+ 'pointerevent_touch-action-none-css_touch-manual',
+ 'pointerevent_touch-action-pan-x-css_touch-manual',
+ 'pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual',
+ 'pointerevent_touch-action-pan-x-pan-y_touch-manual',
+ 'pointerevent_touch-action-pan-y-css_touch-manual',
+ // disable this one because of intermittent failures. see bug 1292134.
+ // 'pointerevent_touch-action-span-test_touch-manual',
+ 'pointerevent_touch-action-svg-test_touch-manual',
+ 'pointerevent_touch-action-table-test_touch-manual',
+ // this one runs as a web-platform-test since it's not a manual test
+ //'pointerevent_touch-action-verification',
+];
+
+// Each entry in |subtests| is loaded in a new window. When loaded, it runs
+// the function returned by apzScriptInjector, which injects some helper JS
+// files into the vanilla unmodified W3C testcase, and simulates the necessary
+// user input to run the test.
+var subtests = [];
+for (var name of testnames) {
+ subtests.push({
+ 'file': 'wpt/' + name + '.html',
+ 'prefs': apz_touch_action_prefs,
+ 'onload': apzScriptInjector(name),
+ });
+}
+
+if (isApzEnabled()) {
+ SimpleTest.waitForExplicitFinish();
+ window.onload = function() {
+ runSubtestsSeriallyInFreshWindows(subtests)
+ .then(SimpleTest.finish, SimpleTest.finish);
+ };
+}
+
+ </script>
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/touch_action_helpers.js b/dom/events/test/pointerevents/touch_action_helpers.js
new file mode 100644
index 0000000000..c46cf369a4
--- /dev/null
+++ b/dom/events/test/pointerevents/touch_action_helpers.js
@@ -0,0 +1,243 @@
+// Some common helpers
+
+function promiseTimeout(delay) {
+ return new Promise(resolve => {
+ setTimeout(resolve, delay);
+ });
+}
+
+function promiseTouchStart(element) {
+ return new Promise(resolve => {
+ element.addEventListener("touchstart", resolve, {
+ passive: true,
+ once: true,
+ });
+ });
+}
+
+async function touchScrollRight(aSelector = "#target0", aX = 20, aY = 20) {
+ const target = document.querySelector(aSelector);
+ const touchStartPromise = promiseTouchStart(document.body);
+ const touchEndPromise = promiseTouchEnd(document.body);
+ dump("Synthesizing horizontal drag\n");
+ await promiseNativePointerDrag(target, "touch", aX + 40, aY, -40, 0);
+ await touchStartPromise;
+ dump("Got touchstart from the horizontal drag\n");
+ await touchEndPromise;
+ dump("Got touchend from the horizontal drag\n");
+}
+
+async function touchScrollDown(aSelector = "#target0", aX = 20, aY = 20) {
+ const target = document.querySelector(aSelector);
+ const touchStartPromise = promiseTouchStart(document.body);
+ const touchEndPromise = promiseTouchEnd(document.body);
+ dump("Synthesizing vertical drag\n");
+ await promiseNativePointerDrag(target, "touch", aX, aY + 40, 0, -40);
+ await touchStartPromise;
+ dump("Got touchstart from the vertical drag\n");
+ await touchEndPromise;
+ dump("Got touchend from the vertical drag\n");
+}
+
+async function tapCompleteAndWaitTestDone() {
+ let testDone = new Promise(resolve => {
+ add_completion_callback(resolve);
+ });
+
+ var button = document.getElementById("btnComplete");
+ button.click();
+ await testDone;
+}
+
+function promiseResetScrollLeft(aSelector = "#target0") {
+ var target = document.querySelector(aSelector);
+ return new Promise(resolve => {
+ target.addEventListener("scroll", function onScroll() {
+ if (target.scrollLeft == 0) {
+ target.removeEventListener("scroll", onScroll);
+ resolve();
+ }
+ });
+ });
+}
+
+// The main body functions to simulate the input events required for the named test
+
+async function pointerevent_touch_action_auto_css_touch_manual() {
+ let testDone = new Promise(resolve => {
+ add_completion_callback(resolve);
+ });
+
+ await touchScrollRight();
+ await promiseApzFlushedRepaints();
+ await touchScrollDown();
+ await testDone;
+}
+
+async function pointerevent_touch_action_button_test_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ await touchScrollRight();
+ let resetScrollLeftPromise = promiseResetScrollLeft();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ // Wait for resetting target0's scrollLeft to avoid the reset break the
+ // following scroll behaviors.
+ await resetScrollLeftPromise;
+ await touchScrollDown("#target0 > button");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > button");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_inherit_child_auto_child_none_touch_manual() {
+ await touchScrollDown("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_inherit_child_none_touch_manual() {
+ await touchScrollDown("#target0 > div");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > div");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_inherit_child_pan_x_child_pan_x_touch_manual() {
+ await touchScrollDown("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_inherit_child_pan_x_child_pan_y_touch_manual() {
+ await touchScrollDown("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_inherit_highest_parent_none_touch_manual() {
+ let testDone = new Promise(resolve => {
+ add_completion_callback(resolve);
+ });
+
+ await touchScrollDown("#target0 > div");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > div");
+ await testDone;
+}
+
+async function pointerevent_touch_action_inherit_parent_none_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await touchScrollRight();
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_none_css_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await touchScrollRight();
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_pan_x_css_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await touchScrollRight();
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_pan_x_pan_y_pan_y_touch_manual() {
+ await touchScrollDown("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0 > div div");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_pan_x_pan_y_touch_manual() {
+ let testDone = new Promise(resolve => {
+ add_completion_callback(resolve);
+ });
+
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await touchScrollRight();
+ await testDone;
+}
+
+async function pointerevent_touch_action_pan_y_css_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await touchScrollRight();
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_span_test_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ await touchScrollRight();
+ let resetScrollLeftPromise = promiseResetScrollLeft();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ // Wait for resetting target0's scrollLeft to avoid the reset break the
+ // following scroll behaviors.
+ await resetScrollLeftPromise;
+ await touchScrollDown("#testspan");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#testspan");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_svg_test_touch_manual() {
+ await touchScrollDown();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ await touchScrollRight();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ await touchScrollDown("#target0", 250, 250);
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#target0", 250, 250);
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+async function pointerevent_touch_action_table_test_touch_manual() {
+ await touchScrollDown("#row1");
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ await touchScrollRight("#row1");
+ let resetScrollLeftPromise = promiseResetScrollLeft();
+ await promiseApzFlushedRepaints();
+ await promiseTimeout(2 * scrollReturnInterval);
+ // Wait for resetting target0's scrollLeft to avoid the reset break the
+ // following scroll behaviors.
+ await resetScrollLeftPromise;
+ await touchScrollDown("#cell3");
+ await promiseApzFlushedRepaints();
+ await touchScrollRight("#cell3");
+ await promiseApzFlushedRepaints();
+ await tapCompleteAndWaitTestDone();
+}
+
+// This the stuff that runs the appropriate body function above
+
+// eslint-disable-next-line no-eval
+var test = eval(_ACTIVE_TEST_NAME.replace(/-/g, "_"));
+waitUntilApzStable().then(test).then(subtestDone, subtestFailed);
diff --git a/dom/events/test/pointerevents/wpt/compat/pointerevent_touch-action_two-finger_interaction-manual.html b/dom/events/test/pointerevents/wpt/compat/pointerevent_touch-action_two-finger_interaction-manual.html
new file mode 100644
index 0000000000..3537e0e1e9
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/compat/pointerevent_touch-action_two-finger_interaction-manual.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: touch-action test for two-finger interaction</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <link rel="help" href="https://compat.spec.whatwg.org/#touch-action" />
+ <meta name="assert" content="Tests that a two-finger pan gesture is cancelled in 'touch-action: pan-x pan-y' but is allowed in 'touch-action: pinch-zoom'"/>
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var event_log = [];
+ var active_pointers = 0;
+
+ function resetTestState() {
+ event_log = [];
+ active_pointers = 0;
+ }
+
+ function run() {
+ var test_pointer_events = [
+ setup_pointerevent_test("two-finger pan on 'touch-action: pan-x pan-y'", ["touch"]),
+ setup_pointerevent_test("two-finger pan on 'touch-action: pinch-zoom'", ["touch"])
+ ];
+ var expected_events = [
+ "pointerdown@black, pointerdown@black, pointerup@black, pointerup@black",
+ "pointerdown@grey, pointerdown@grey, pointercancel@grey, pointercancel@grey"
+ ];
+ var current_test_index = 0;
+
+ on_event(document.getElementById("done"), "click", function() {
+ test_pointer_events[current_test_index].step(function () {
+ assert_equals(active_pointers, 0);
+ assert_equals(event_log.join(", "), expected_events[current_test_index]);
+ });
+ event_log = [];
+
+ test_pointer_events[current_test_index++].done();
+ });
+
+ var targets = [document.getElementById("black"), document.getElementById("grey")];
+
+ ["pointerdown", "pointerup", "pointercancel"].forEach(function(eventName) {
+ targets.forEach(function(target){
+ on_event(target, eventName, function (event) {
+ event_log.push(event.type + "@" + event.target.id);
+
+ if (event.type == "pointerdown") {
+ active_pointers++;
+
+ } else {
+ active_pointers--;
+ }
+ });
+ });
+ });
+ }
+ </script>
+ <style>
+ .box {
+ width: 250px;
+ height: 150px;
+ float: left;
+ margin: 10px;
+ }
+
+ #black {
+ touch-action: pan-x pan-y;
+ background-color: black;
+ }
+
+ #grey {
+ touch-action: pinch-zoom;
+ background-color: grey;
+ }
+
+ #done {
+ float: left;
+ padding: 20px;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: touch-action test for two-finger interaction</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Tests that a two-finger pan gesture is cancelled in 'touch-action: pan-x pan-y' but is allowed in 'touch-action: pinch-zoom'
+ </h4>
+ <ol>
+ <li>Touch on Black with two fingers and drag both fingers down at same speed.</li>
+ <li>Tap on Done.</li>
+ <li>Touch on Grey with two fingers and drag both fingers down at same speed.</li>
+ <li>Tap on Done.</li>
+ </ol>
+ <div class="box" id="black"></div>
+ <input type="button" id="done" value="Done" />
+ <div class="box" id="grey"></div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/html/pointerevent_drag_interaction-manual.html b/dom/events/test/pointerevents/wpt/html/pointerevent_drag_interaction-manual.html
new file mode 100644
index 0000000000..1a80d239b8
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/html/pointerevent_drag_interaction-manual.html
@@ -0,0 +1,103 @@
+<html>
+ <head>
+ <title>Pointer Events interaction with drag and drop</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <script>
+ var eventList = ['pointerdown', 'pointerup', 'pointercancel', 'gotpointercapture', 'lostpointercapture', 'dragstart', 'mousedown'];
+
+ PhaseEnum = {
+ DndWithoutCapture: 0,
+ DndWithCapture: 1,
+ DndWithCaptureMouse: 2,
+ DndPrevented: 3,
+ Done: 4,
+ };
+ var phase = PhaseEnum.DndWithoutCapture;
+ var received_events = [];
+ var pointerId = -1;
+
+ function resetTestState() {
+ phase = PhaseEnum.DndWithoutCapture;
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointer events vs drag and drop", ['mouse']);
+
+ var target0 = document.querySelector('#target0');
+ var target1 = document.querySelector('#target1');
+
+ function handleEvent(e) {
+ if (e.type == 'pointerdown') {
+ received_events = [];
+ if (phase == PhaseEnum.DndWithCapture) {
+ target0.setPointerCapture(e.pointerId);
+ } else if (phase == PhaseEnum.DndWithCaptureMouse) {
+ pointerId = e.pointerId;
+ }
+ }
+ if (e.type == 'mousedown') {
+ if (phase == PhaseEnum.DndWithCaptureMouse) {
+ target0.setPointerCapture(pointerId);
+ }
+ }
+ received_events.push(e.type + "@" + e.target.id);
+ if (e.type == 'dragstart') {
+ e.dataTransfer.setData('text/plain', 'dragstart test');
+ if (phase == PhaseEnum.DndPrevented)
+ e.preventDefault();
+ }
+ if (phase == PhaseEnum.DndWithoutCapture && e.type == 'pointercancel') {
+ phase++;
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, dragstart@target0, pointercancel@target0", "Pointercancel should be fired with the expected order when drag operation starts.");
+ }, "Pointercancel when drag operation starts");
+ } else if (phase == PhaseEnum.DndWithCapture && e.type == 'lostpointercapture') {
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, gotpointercapture@target0, dragstart@target0, pointercancel@target0, lostpointercapture@target0", "Pointercancel and lostpointercapture should be fired with the expected order when drag operation starts.");
+ }, "Pointercancel while capturing when drag operation starts");
+ phase++;
+ } else if (phase == PhaseEnum.DndWithCaptureMouse && e.type == 'lostpointercapture') {
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, gotpointercapture@target0, dragstart@target0, pointercancel@target0, lostpointercapture@target0", "Pointercancel and lostpointercapture should be fired with the expected order when drag operation starts.");
+ }, "Pointercancel while capturing on mousedown when drag operation starts");
+ phase++;
+ } else if (phase == PhaseEnum.DndPrevented && e.type == 'pointerup') {
+ test(() => {
+ assert_equals(received_events.join(', '), "pointerdown@target0, mousedown@target0, dragstart@target0, pointerup@target1", "Pointerevent stream shouldn't get interrupted when drag is prevented.");
+ }, "Pointerevent stream when drag is prevented.");
+ phase++;
+ test_pointerEvent.done();
+ }
+ }
+ eventList.forEach(function(eventName) {
+ on_event(target0, eventName, handleEvent);
+ on_event(target1, eventName, handleEvent);
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events interaction with drag and drop</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks that the pointercancel (and if needed lostpointercapture) is dispatched when drag starts.
+ <ol>
+ <li>Press down on the black square.</li>
+ <li>Move your pointer to purple square and release.</li>
+ <li>Repeat the first two steps.</li>
+ <li>Repeat the first two steps once again.</li>
+ <li>Repeat the first two steps once again.</li>
+ </ol>
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div draggable="true" id="target0"></div>
+ <div id="target1"></div>
+ </div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/idlharness.html b/dom/events/test/pointerevents/wpt/idlharness.html
new file mode 100644
index 0000000000..a4ba4c35f5
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/idlharness.html
@@ -0,0 +1,104 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>idlharness test</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src="/resources/WebIDLParser.js"></script>
+<script src="/resources/idlharness.js"></script>
+
+<pre id='untested_idl' style='display:none'>
+[PrimaryGlobal]
+interface Window {
+};
+
+[TreatNonObjectAsNull]
+callback EventHandlerNonNull = any (Event event);
+typedef EventHandlerNonNull? EventHandler;
+
+[NoInterfaceObject]
+interface GlobalEventHandlers {
+};
+Window implements GlobalEventHandlers;
+
+interface Navigator {
+};
+
+interface Element {
+};
+
+interface HTMLElement : Element {
+};
+HTMLElement implements GlobalEventHandlers;
+
+interface Document {
+};
+Document implements GlobalEventHandlers;
+
+interface MouseEvent {
+};
+
+</pre>
+
+<pre id='idl'>
+dictionary PointerEventInit : MouseEventInit {
+ long pointerId = 0;
+ double width = 1;
+ double height = 1;
+ float pressure = 0;
+ float tangentialPressure = 0;
+ long tiltX = 0;
+ long tiltY = 0;
+ long twist = 0;
+ DOMString pointerType = "";
+ boolean isPrimary = false;
+};
+
+[Constructor(DOMString type, optional PointerEventInit eventInitDict)]
+interface PointerEvent : MouseEvent {
+ readonly attribute long pointerId;
+ readonly attribute double width;
+ readonly attribute double height;
+ readonly attribute float pressure;
+ readonly attribute float tangentialPressure;
+ readonly attribute long tiltX;
+ readonly attribute long tiltY;
+ readonly attribute long twist;
+ readonly attribute DOMString pointerType;
+ readonly attribute boolean isPrimary;
+};
+
+partial interface Element {
+ void setPointerCapture(long pointerId);
+ void releasePointerCapture(long pointerId);
+ boolean hasPointerCapture(long pointerId);
+};
+
+partial interface GlobalEventHandlers {
+ attribute EventHandler ongotpointercapture;
+ attribute EventHandler onlostpointercapture;
+ attribute EventHandler onpointerdown;
+ attribute EventHandler onpointermove;
+ attribute EventHandler onpointerup;
+ attribute EventHandler onpointercancel;
+ attribute EventHandler onpointerover;
+ attribute EventHandler onpointerout;
+ attribute EventHandler onpointerenter;
+ attribute EventHandler onpointerleave;
+};
+
+partial interface Navigator {
+ readonly attribute long maxTouchPoints;
+};
+</pre>
+<script>
+ var idl_array = new IdlArray();
+ idl_array.add_untested_idls(document.getElementById("untested_idl").textContent);
+ idl_array.add_idls(document.getElementById("idl").textContent);
+
+ // Note that I don't bother including Document here because there are still
+ // a bunch of differences between browsers around Document vs HTMLDocument.
+ idl_array.add_objects({
+ Window: ["window"],
+ Navigator: ["navigator"]});
+ idl_array.test();
+</script>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_attributes_hoverable_pointers-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_attributes_hoverable_pointers-manual.html
new file mode 100644
index 0000000000..0922ae7448
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_attributes_hoverable_pointers-manual.html
@@ -0,0 +1,143 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events properties tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ var detected_eventTypes = {};
+ var eventList = ['pointerover', 'pointerenter', 'pointermove', 'pointerdown', 'pointerup', 'pointerout', 'pointerleave'];
+ var expectedPointerId = NaN;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ document.getElementById("square1").style.visibility = 'visible';
+ document.getElementById('innerFrame').contentDocument.getElementById("square2").style.visibility = 'hidden';
+ expectedPointerId = NaN;
+ }
+ function checkPointerEventAttributes(event, targetBoundingClientRect, testNamePrefix) {
+ if (detected_eventTypes[event.type])
+ return;
+ var expectedEventType = eventList[Object.keys(detected_eventTypes).length];
+ detected_eventTypes[event.type] = true;
+ var pointerTestName = testNamePrefix + ' ' + expectedPointerType + ' ' + expectedEventType;
+
+ detected_pointertypes[event.pointerType] = true;
+
+ test(function() {
+ assert_equals(event.type, expectedEventType, "Event.type should be " + expectedEventType)
+ }, pointerTestName + "'s type should be " + expectedEventType);
+
+ // Test button and buttons
+ if (event.type == 'pointerdown') {
+ test(function() {
+ assert_true(event.button == 0, "Button attribute is 0")
+ }, pointerTestName + "'s button attribute is 0 when left mouse button is pressed.");
+ test(function() {
+ assert_true(event.buttons == 1, "Buttons attribute is 1")
+ }, pointerTestName + "'s buttons attribute is 1 when left mouse button is pressed.");
+ } else if (event.type == 'pointerup') {
+ test(function() {
+ assert_true(event.button == 0, "Button attribute is 0")
+ }, pointerTestName + "'s button attribute is 0 when left mouse button is just released.");
+ test(function() {
+ assert_true(event.buttons == 0, "Buttons attribute is 0")
+ }, pointerTestName + "'s buttons attribute is 0 when left mouse button is just released.");
+ } else {
+ test(function() {
+ assert_true(event.button == -1, "Button attribute is -1")
+ }, pointerTestName + "'s button is -1 when mouse buttons are in released state.");
+ test(function() {
+ assert_true(event.buttons == 0, "Buttons attribute is 0")
+ }, pointerTestName + "'s buttons is 0 when mouse buttons are in released state.");
+ }
+
+ // Test clientX and clientY
+ if (event.type != 'pointerout' && event.type != 'pointerleave' ) {
+ test(function () {
+ assert_true(event.clientX >= targetBoundingClientRect.left && event.clientX < targetBoundingClientRect.right && event.clientY >= targetBoundingClientRect.top && event.clientY < targetBoundingClientRect.bottom, "ClientX/Y should be in the boundaries of the box");
+ }, pointerTestName + "'s ClientX and ClientY attributes are correct.");
+ } else {
+ test(function () {
+ assert_true(event.clientX < targetBoundingClientRect.left || event.clientX > targetBoundingClientRect.right - 1 || event.clientY < targetBoundingClientRect.top || event.clientY > targetBoundingClientRect.bottom - 1, "ClientX/Y should be out of the boundaries of the box");
+ }, pointerTestName + "'s ClientX and ClientY attributes are correct.");
+ }
+
+ check_PointerEvent(event, testNamePrefix);
+
+ // Test isPrimary value
+ test(function () {
+ assert_equals(event.isPrimary, true, "isPrimary should be true");
+ }, pointerTestName + ".isPrimary attribute is correct.");
+
+ // Test pointerId value
+ if (isNaN(expectedPointerId)) {
+ expectedPointerId = event.pointerId;
+ } else {
+ test(function () {
+ assert_equals(event.pointerId, expectedPointerId, "pointerId should remain the same for the same active pointer");
+ }, pointerTestName + ".pointerId should be the same as previous pointer events for this active pointer.");
+ }
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", HOVERABLE_POINTERS);
+ var square1 = document.getElementById("square1");
+ var rectSquare1 = square1.getBoundingClientRect();
+ var innerFrame = document.getElementById('innerFrame');
+ var square2 = innerFrame.contentDocument.getElementById('square2');
+ var rectSquare2 = square2.getBoundingClientRect();
+
+ eventList.forEach(function(eventName) {
+ on_event(square1, eventName, function (event) {
+ if (square1.style.visibility == 'hidden')
+ return;
+ checkPointerEventAttributes(event, rectSquare1, "");
+ if (Object.keys(detected_eventTypes).length == eventList.length) {
+ square1.style.visibility = 'hidden';
+ detected_eventTypes = {};
+ square2.style.visibility = 'visible';
+ expectedPointerId = NaN;
+ }
+ });
+ on_event(square2, eventName, function (event) {
+ checkPointerEventAttributes(event, rectSquare2, "Inner frame ");
+ if (Object.keys(detected_eventTypes).length == eventList.length) {
+ square2.style.visibility = 'hidden';
+ test_pointerEvent.done();
+ }
+ });
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events hoverable pointer attributes test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks the properties of hoverable pointer events. If you are using hoverable pen don't leave the range of digitizer while doing the instructions.
+ <ol>
+ <li>Move your pointer over the black square and click on it.</li>
+ <li>Then move it off the black square so that it disappears.</li>
+ <li>When red square appears move your pointer over the red square and click on it.</li>
+ <li>Then move it off the red square.</li>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="square1" class="square"></div>
+ <iframe id="innerFrame" src="resources/pointerevent_attributes_hoverable_pointers-iframe.html"></iframe>
+ <div class="spacer"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
+
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_attributes_nohover_pointers-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_attributes_nohover_pointers-manual.html
new file mode 100644
index 0000000000..0fd7904ef0
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_attributes_nohover_pointers-manual.html
@@ -0,0 +1,126 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events properties tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ var detected_eventTypes = {};
+ var eventList = ['pointerover', 'pointerenter', 'pointerdown', 'pointerup', 'pointerout', 'pointerleave'];
+ var expectedPointerId = NaN;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ document.getElementById("square1").style.visibility = 'visible';
+ document.getElementById('innerFrame').contentDocument.getElementById("square2").style.visibility = 'hidden';
+ }
+ function checkPointerEventAttributes(event, targetBoundingClientRect, testNamePrefix) {
+ if (detected_eventTypes[event.type])
+ return;
+ var expectedEventType = eventList[Object.keys(detected_eventTypes).length];
+ detected_eventTypes[event.type] = true;
+ var pointerTestName = testNamePrefix + ' ' + expectedPointerType + ' ' + expectedEventType;
+
+ detected_pointertypes[event.pointerType] = true;
+
+ test(function() {
+ assert_equals(event.type, expectedEventType, "Event.type should be " + expectedEventType)
+ }, pointerTestName + "'s type should be " + expectedEventType);
+
+ // Test button and buttons
+ test(function() {
+ assert_true(event.button == 0, "Button attribute is 0")
+ }, pointerTestName + "'s button attribute is 0 when left mouse button is pressed.");
+
+ if (event.type == 'pointerdown' || event.type == 'pointerover' || event.type == 'pointerenter') {
+ test(function() {
+ assert_true(event.buttons == 1, "Buttons attribute is 1")
+ }, pointerTestName + "'s buttons attribute is 1 when left mouse button is pressed.");
+ } else {
+ test(function() {
+ assert_true(event.buttons == 0, "Buttons attribute is 0")
+ }, pointerTestName + "'s buttons is 0 when mouse buttons are in released state.");
+ }
+
+ // Test clientX and clientY
+ test(function () {
+ assert_true(event.clientX >= targetBoundingClientRect.left && event.clientX < targetBoundingClientRect.right && event.clientY >= targetBoundingClientRect.top && event.clientY < targetBoundingClientRect.bottom, "ClientX/Y should be in the boundaries of the box");
+ }, pointerTestName + "'s ClientX and ClientY attributes are correct.");
+
+ check_PointerEvent(event, testNamePrefix);
+
+ // Test isPrimary
+ test(function () {
+ assert_equals(event.isPrimary, true, "isPrimary should be true");
+ }, pointerTestName + ".isPrimary attribute is correct.");
+
+ // Test pointerId value
+ if (isNaN(expectedPointerId)) {
+ expectedPointerId = event.pointerId;
+ } else {
+ test(function () {
+ assert_equals(event.pointerId, expectedPointerId, "pointerId should remain the same for the same active pointer");
+ }, pointerTestName + ".pointerId should be the same as previous pointer events for this active pointer.");
+ }
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", NOHOVER_POINTERS);
+ var square1 = document.getElementById("square1");
+ var rectSquare1 = square1.getBoundingClientRect();
+ var innerFrame = document.getElementById('innerFrame');
+ var square2 = innerFrame.contentDocument.getElementById('square2');
+ var rectSquare2 = square2.getBoundingClientRect();
+
+ eventList.forEach(function(eventName) {
+ on_event(square1, eventName, function (event) {
+ if (square1.style.visibility == 'hidden')
+ return;
+ checkPointerEventAttributes(event, rectSquare1, "");
+ if (Object.keys(detected_eventTypes).length == eventList.length) {
+ square1.style.visibility = 'hidden';
+ detected_eventTypes = {};
+ square2.style.visibility = 'visible';
+ expectedPointerId = NaN;
+ }
+ });
+ on_event(square2, eventName, function (event) {
+ checkPointerEventAttributes(event, rectSquare2, "Inner frame ");
+ if (Object.keys(detected_eventTypes).length == eventList.length) {
+ square2.style.visibility = 'hidden';
+ test_pointerEvent.done();
+ }
+ });
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events no-hover pointer attributes test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks the properties of pointer events that do not support hover.
+ <ol>
+ <li>Tap the black square.</li>
+ <li>Then move it off the black square so that it disappears.</li>
+ <li>When the red square appears tap on that as well.</li>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="square1" class="square"></div>
+ <iframe id="innerFrame" src="resources/pointerevent_attributes_hoverable_pointers-iframe.html"></iframe>
+ <div class="spacer"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
+
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_boundary_events_in_capturing-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_boundary_events_in_capturing-manual.html
new file mode 100644
index 0000000000..0de4d55ed1
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_boundary_events_in_capturing-manual.html
@@ -0,0 +1,97 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events boundary events in capturing tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ var eventList = All_Pointer_Events;
+ PhaseEnum = {
+ WaitingForDown: "down",
+ WaitingForFirstMove: "firstMove",
+ WaitingForSecondMove: "secondMove",
+ WaitingForUp: "up"
+ }
+ var phase = PhaseEnum.WaitingForDown;
+ var eventsRecieved = [];
+
+ function resetTestState() {
+ eventsRecieved = [];
+ phase = PhaseEnum.WaitingForDown;
+ }
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent boundary events in capturing", ALL_POINTERS);
+ var target = document.getElementById("target0");
+ var listener = document.getElementById("listener");
+
+ eventList.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ if (phase == PhaseEnum.WaitingForDown) {
+ if (eventName == 'pointerdown') {
+ listener.setPointerCapture(event.pointerId);
+ phase = PhaseEnum.WaitingForFirstMove;
+ }
+ } else if (phase == PhaseEnum.WaitingForUp) {
+ if (event.type == 'pointerup')
+ test_pointerEvent.done();
+ } else {
+ eventsRecieved.push(event.type + '@target');
+ if (phase == PhaseEnum.WaitingForSecondMove && event.type == 'pointermove') {
+ test(function () {
+ checkPointerEventType(event);
+ assert_array_equals(eventsRecieved, ['lostpointercapture@listener', 'pointerout@listener', 'pointerleave@listener', 'pointerover@target', 'pointerenter@target', 'pointermove@target'],
+ 'lostpointercapture and pointerout/leave should be dispatched to the capturing target and pointerover/enter should be dispatched to the hit-test element before the first pointermove event after releasing pointer capture');
+ }, expectedPointerType + " pointer events boundary events when releasing capture");
+ phase = PhaseEnum.WaitingForUp;
+ }
+ }
+ });
+ on_event(listener, eventName, function (event) {
+ if (phase == PhaseEnum.WaitingForDown)
+ return;
+ eventsRecieved.push(event.type + '@listener');
+ if (phase == PhaseEnum.WaitingForFirstMove && eventName == 'pointermove') {
+ test(function () {
+ checkPointerEventType(event);
+ assert_array_equals(eventsRecieved, ['pointerout@target', 'pointerleave@target', 'pointerover@listener', 'pointerenter@listener', 'gotpointercapture@listener', 'pointermove@listener'],
+ 'pointerout/leave should be dispatched to the previous target and pointerover/enter and gotpointercapture should be dispatched to the capturing element before the first captured pointermove event');
+ }, expectedPointerType + " pointer events boundary events when receiving capture");
+ listener.releasePointerCapture(event.pointerId);
+ eventsRecieved = [];
+ phase = PhaseEnum.WaitingForSecondMove;
+ }
+ });
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events boundary events in capturing</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks the boundary events of pointer events while the capturing changes. If you are using hoverable pen don't leave the range of digitizer while doing the instructions.
+ <ol>
+ <li>Move your pointer over the black square</li>
+ <li>Press down the pointer (i.e. press left button with mouse or touch the screen with finger or pen).</li>
+ <li>Drag the pointer within the black square.</li>
+ <li>Release the pointer.</li>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="target0" class="touchActionNone">
+ </div>
+ <div id="listener">Do not hover over or touch this element. </div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
+
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html
new file mode 100644
index 0000000000..04d56cb7a5
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_change-touch-action-onpointerdown_touch-manual.html
@@ -0,0 +1,135 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Change touch-action on pointerdown</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ background: black;
+ width: 700px;
+ height: 430px;
+ color: white;
+ overflow-y: auto;
+ overflow-x: auto;
+ white-space: nowrap;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4>Test Description: Press and hold your touch. Try to scroll text in any direction.
+ Then release your touch and try to scroll again. Expected: no panning.
+ </h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0" style="touch-action: auto;">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var styleIsChanged = false;
+ var scrollIsReceived = false;
+ var firstTouchCompleted = false;
+ var countToPass = 50;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ setup({ explicit_done: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, 'scroll', function(event) {
+ if(!scrollIsReceived && firstTouchCompleted) {
+ test(function() {
+ failOnScroll();
+ }, "scroll was received while shouldn't");
+ scrollIsReceived = true;
+ }
+ done();
+ });
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ if(!styleIsChanged) {
+ var before = document.getElementById('target0').style.touchAction;
+
+ document.getElementById('target0').style.touchAction = 'none';
+
+ var after = document.getElementById('target0').style.touchAction;
+
+ test(function() {
+ assert_true(before != after, "touch-action was changed");
+ }, "touch-action was changed");
+
+ styleIsChanged = true;
+ }
+ });
+
+ on_event(target0, 'pointerup', function(event) {
+ firstTouchCompleted = true;
+ });
+ }
+ </script>
+ <h1>touch-action: auto to none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_constructor.html b/dom/events/test/pointerevents/wpt/pointerevent_constructor.html
new file mode 100644
index 0000000000..b2a779d1f7
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_constructor.html
@@ -0,0 +1,106 @@
+<!doctype html>
+<html>
+ <head>
+ <title>PointerEvent: Constructor test</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body>
+ <h1>PointerEvent: Dispatch custom event</h1>
+ <h4>Test Description: This test checks if PointerEvent constructor works properly using synthetic pointerover and pointerout events. For valid results, this test must be run without generating real (trusted) pointerover or pointerout events on the black rectangle below.</h4>
+ <div id="target0"></div>
+ <script>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ async_test(function() {
+ var target0 = document.getElementById("target0");
+ // set values for non-default constructor
+ var testBubbles = true;
+ var testCancelable = true;
+ var testPointerId = 42;
+ var testPointerType = 'pen';
+ var testClientX = 300;
+ var testClientY = 500;
+ var testWidth = 3;
+ var testHeight = 5;
+ var testTiltX = -45;
+ var testTiltY = 30;
+ var testButton = 0;
+ var testButtons = 1;
+ var testPressure = 0.4;
+ var testIsPrimary = true;
+
+ on_event(target0, "pointerover", this.step_func(function(event) {
+ detected_pointertypes[ event.pointerType ] = true;
+ generate_tests(assert_equals, [
+ ["custom bubbles", event.bubbles, testBubbles],
+ ["custom cancelable", event.cancelable, testCancelable],
+ ["custom pointerId", event.pointerId, testPointerId],
+ ["custom pointerType", event.pointerType, testPointerType],
+ ["custom button", event.button, testButton],
+ ["custom buttons", event.buttons, testButtons],
+ ["custom width", event.width, testWidth],
+ ["custom height", event.height, testHeight],
+ ["custom clientX", event.clientX, testClientX],
+ ["custom clientY", event.clientY, testClientY],
+ ["custom tiltX", event.tiltX, testTiltX],
+ ["custom tiltY", event.tiltY, testTiltY],
+ ["custom isPrimary", event.isPrimary, testIsPrimary]
+ ]);
+ test(function() {
+ assert_approx_equals(event.pressure, testPressure, 0.00000001, "custom pressure: ");
+ }, "custom pressure: ");
+ }));
+
+ on_event(target0, "pointerout", this.step_func(function(event) {
+ generate_tests(assert_equals, [
+ ["default pointerId", event.pointerId, 0],
+ ["default pointerType", event.pointerType, ""],
+ ["default width", event.width, 1],
+ ["default height", event.height, 1],
+ ["default tiltX", event.tiltX, 0],
+ ["default tiltY", event.tiltY, 0],
+ ["default pressure", event.pressure, 0],
+ ["default isPrimary", event.isPrimary, false]
+ ]);
+ }));
+
+ on_event(window, "load", this.step_func_done(function() {
+ assert_not_equals(window.PointerEvent, undefined);
+
+ var pointerEventCustom = new PointerEvent("pointerover",
+ {bubbles: testBubbles,
+ cancelable: testCancelable,
+ pointerId: testPointerId,
+ pointerType: testPointerType,
+ width: testWidth,
+ height: testHeight,
+ clientX: testClientX,
+ clientY: testClientY,
+ tiltX: testTiltX,
+ tiltY: testTiltY,
+ button: testButton,
+ buttons: testButtons,
+ pressure: testPressure,
+ isPrimary: testIsPrimary
+ });
+ // A PointerEvent created with a PointerEvent constructor must have all its attributes set to the corresponding values provided to the constructor.
+ // For attributes where values are not provided to the constructor, the corresponding default values must be used.
+ // TA: 12.1
+ target0.dispatchEvent(pointerEventCustom);
+ var pointerEventDefault = new PointerEvent("pointerout");
+ target0.dispatchEvent(pointerEventDefault);
+ }, "PointerEvent constructor"));
+ })
+ </script>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html
new file mode 100644
index 0000000000..eb758c7073
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_multiple_primary_pointers_boundary_events-manual.html
@@ -0,0 +1,145 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Boundary compatibility events for multiple primary pointers</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When more than one primary pointers are active, each will have an independent sequence of pointer boundary events but the compatibilty mouse boundary events have their own sequence."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var test_pointerEvent = async_test("Multi-pointer boundary compat events");
+ add_completion_callback(end_of_test);
+
+ var detected_pointertypes = {};
+ var event_log = [];
+
+ // These two ids help us detect two different pointing devices.
+ var first_entry_pointer_id = -1;
+ var second_entry_pointer_id = -1;
+
+ // Current node for each pointer id
+ var current_node_for_id = {};
+
+ function end_of_test() {
+ showLoggedEvents();
+ showPointerTypes();
+ }
+
+ function end_of_interaction() {
+ test(function () {
+ assert_equals(event_log.join(", "),
+ "mouseover@target0, mouseenter@target0, mouseout@target0, mouseleave@target0, " +
+ "mouseover@target1, mouseenter@target1, mouseout@target1, mouseleave@target1, " +
+ "mouseover@target0, mouseenter@target0, mouseout@target0, mouseleave@target0"
+ );
+ }, "Event log");
+
+ test_pointerEvent.done(); // complete test
+ }
+
+ function log_event(label) {
+ event_log.push(label);
+ }
+
+ function run() {
+ on_event(document.getElementById("done"), "click", end_of_interaction);
+
+ var target_list = ["target0", "target1"];
+ var pointer_event_list = ["pointerenter", "pointerleave", "pointerover", "pointerout", "pointerdown"];
+ var mouse_event_list = ["mouseenter", "mouseleave", "mouseover", "mouseout"];
+
+ target_list.forEach(function(targetId) {
+ var target = document.getElementById(targetId);
+
+ pointer_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ var label = event.type + "@" + targetId;
+
+ detected_pointertypes[event.pointerType] = true;
+
+ if (!event.isPrimary) {
+ test(function () {
+ assert_unreached("Non-primary pointer " + label);
+ }, "Non-primary pointer " + label);
+ }
+
+ if (event.type === "pointerenter") {
+ var pointer_id = event.pointerId;
+ if (current_node_for_id[pointer_id] !== undefined) {
+ test(function () {
+ assert_unreached("Double entry " + label);
+ }, "Double entry " + label);
+ }
+ current_node_for_id[pointer_id] = event.target;
+
+ // Test that two different pointing devices are used
+ if (first_entry_pointer_id === -1) {
+ first_entry_pointer_id = pointer_id;
+ } else if (second_entry_pointer_id === -1) {
+ second_entry_pointer_id = pointer_id;
+ test(function () {
+ assert_true(first_entry_pointer_id !== second_entry_pointer_id);
+ }, "Different pointing devices");
+ }
+ } else if (event.type === "pointerleave") {
+ var pointer_id = event.pointerId;
+ if (current_node_for_id[pointer_id] !== event.target) {
+ test(function () {
+ assert_unreached("Double exit " + label);
+ }, "Double exit " + label);
+ }
+ current_node_for_id[pointer_id] = undefined;
+ }
+ });
+ });
+
+ mouse_event_list.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ log_event(event.type + "@" + targetId);
+ });
+ });
+ });
+ }
+ </script>
+ <style>
+ #target0, #target1 {
+ margin: 20px;
+ }
+
+ #done {
+ margin: 20px;
+ border: 2px solid black;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Boundary compatibility events for multiple primary pointers</h1>
+ <h4>
+ When more than one primary pointers are active, each will have an independent sequence of pointer boundary events but the compatibilty mouse boundary events have their own sequence.
+ </h4>
+ Instruction:
+ <ol>
+ <li>Move the mouse directly into Target0 (without going through Target1), and then leave the mouse there unmoved.</li>
+ <li>Tap directly on Target1 with a finger or a stylus, and then lift the finger/stylus off the screen/digitizer without crossing Target1 boundary.</li>
+ <li>Move the mouse into Target0 (if not there already) and move inside it.</li>
+ <li>Click Done (without passing over Target1).</li>
+ </ol>
+ <div id="done">
+ Done
+ </div>
+ <div id="target0">
+ Target0
+ </div>
+ <div id="target1">
+ Target1
+ </div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html
new file mode 100644
index 0000000000..3640cb6f6b
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_pointerId_scope-manual.html
@@ -0,0 +1,82 @@
+<!doctype html>
+<html>
+ <!--
+Test cases for Pointer Events v1 spec
+This document references Test Assertions (abbrev TA below) written by Cathy Chan
+http://www.w3.org/wiki/PointerEvents/TestAssertions
+-->
+ <head>
+ <title>Pointer Events pointerdown tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointerId of an active pointer is the same across iframes");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var pointerover_pointerId = null;
+ var pointerover_pointerType = null;
+
+ var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
+ var receivedEvents = {};
+ var receivedEventsInnerFrame = {};
+
+
+ function checkPointerId(event, inner) {
+ detected_pointertypes[event.pointerType] = true;
+ var eventName = (inner ? "inner frame " : "" ) + event.type;
+ test_pointerEvent.step(function() {
+ assert_equals(event.pointerId, pointerover_pointerId, "PointerId of " + eventName + " is not correct");
+ assert_equals(event.pointerType, pointerover_pointerType, "PointerType of " + eventName + " is not correct");
+ }, eventName + ".pointerId were the same as first pointerover");
+ }
+
+ on_event(window, "message", function(event) {
+ var pe_event = JSON.parse(event.data);
+ receivedEventsInnerFrame[pe_event.type] = 1;
+ checkPointerId(pe_event, true);
+ if (Object.keys(receivedEvents).length == eventList.length && Object.keys(receivedEventsInnerFrame).length == eventList.length)
+ test_pointerEvent.done();
+ });
+
+ eventList.forEach(function(eventName) {
+ on_event(target0, eventName, function (event) {
+ if (pointerover_pointerId === null && event.type == 'pointerover') {
+ pointerover_pointerId = event.pointerId;
+ pointerover_pointerType = event.pointerType;
+ } else {
+ checkPointerId(event, false);
+ }
+ receivedEvents[event.type] = 1;
+ });
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events pointerdown tests</h1>
+ Complete the following actions:
+ <ol>
+ <li>Start with your pointing device outside of black box, then move it into black box. If using touch just press in black box and don't release.
+ <li>Move your pointing device into purple box (without leaving the digitizer range if you are using hover supported pen or without releasing touch if using touch). Then move it out of the purple box.
+ </ol>
+ <div id="target0" class="touchActionNone">
+ </div>
+ <iframe src="resources/pointerevent_pointerId_scope-iframe.html" id="innerframe"></iframe>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointercancel_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointercancel_touch-manual.html
new file mode 100644
index 0000000000..70a65eeb5c
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_pointercancel_touch-manual.html
@@ -0,0 +1,77 @@
+<!doctype html>
+<html>
+ <head>
+ <title>PointerCancel - touch</title>
+ <meta name="viewport" content="width=device-width">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable" onload="run()">
+ <h1>pointercancel test</h1>
+ <h3>Warning: this test works properly only for devices that have touchscreen</h3>
+ <h4>
+ Test Description: This test checks if pointercancel event triggers.
+ <p>Start touch over the black rectangle and then move your finger to scroll the page.</p>
+ </h4>
+ <p>
+ <div id="target0" style="background: black"></div>
+ <script>
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointercancel event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var pointerdown_event = null;
+ var pointercancel_event = null;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, "pointerdown", function (event) {
+ pointerdown_event = event;
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, "pointercancel", function (event) {
+ pointercancel_event = event;
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointerdown_event, null, "pointerdown was received: ");
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerId should be the same for pointerdown and pointercancel");
+ assert_equals(event.pointerType, pointerdown_event.pointerType, "pointerType should be the same for pointerdown and pointercancel");
+ assert_equals(event.isPrimary, pointerdown_event.isPrimary, "isPrimary should be the same for pointerdown and pointercancel");
+ check_PointerEvent(event);
+ });
+ });
+
+ on_event(target0, "pointerout", function (event) {
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointercancel_event, null, "pointercancel was received before pointerout: ");
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerId should be the same for pointerout and pointercancel");
+ assert_equals(event.pointerType, pointerdown_event.pointerType, "pointerType should be the same for pointerout and pointercancel");
+ assert_equals(event.isPrimary, pointerdown_event.isPrimary, "isPrimary should be the same for pointerout and pointercancel");
+ });
+ });
+
+ on_event(target0, "pointerleave", function (event) {
+ test_pointerEvent.step(function () {
+ assert_not_equals(pointercancel_event, null, "pointercancel was received before pointerleave: ");
+ assert_equals(event.pointerId, pointerdown_event.pointerId, "pointerId should be the same for pointerleave and pointercancel");
+ assert_equals(event.pointerType, pointerdown_event.pointerType, "pointerType should be the same for pointerleave and pointercancel");
+ assert_equals(event.isPrimary, pointerdown_event.isPrimary, "isPrimary should be the same for pointerleave and pointercancel");
+ });
+ test_pointerEvent.done();
+ });
+ }
+ </script>
+ <h1>Pointer Events pointercancel Tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointerleave_after_pointercancel_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointerleave_after_pointercancel_touch-manual.html
new file mode 100644
index 0000000000..56be26549f
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_pointerleave_after_pointercancel_touch-manual.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerleave after pointercancel</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable" onload="run()">
+ <h2>pointerleave after pointercancel</h2>
+ <h4>Test Description: This test checks if pointerleave event triggers after pointercancel. Start touch on the black rectangle and move your touch to scroll in any direction. </h4>
+ <p>Note: this test is for touch devices only</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerleave = async_test("pointerleave event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var eventTested = false;
+ var pointercancel_event = null;
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, "pointercancel", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ pointercancel_event = event;
+ });
+
+ // After firing the pointercancel event the pointerleave event must be dispatched.
+ // TA: 4.3.1
+ on_event(target0, "pointerleave", function (event) {
+ if(event.pointerType == 'touch') {
+ if(pointercancel_event != null) {
+ if(eventTested == false) {
+ test_pointerleave.step(function() {
+ assert_equals(event.pointerType, pointercancel_event.pointerType, "pointerType is same for pointercancel and pointerleave");
+ assert_equals(event.isPrimary, pointercancel_event.isPrimary, "isPrimary is same for pointercancel and pointerleave");
+ });
+ eventTested = true;
+ test_pointerleave.done();
+ }
+ }
+ else {
+ test_pointerleave.step(function() {
+ assert_unreached("pointerleave received before pointercancel");
+ }, "pointerleave received before pointercancel");
+ }
+ }
+ });
+ }
+
+ </script>
+ <h1>Pointer Events pointerleave tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointerleave_pen-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointerleave_pen-manual.html
new file mode 100644
index 0000000000..38a2f69792
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_pointerleave_pen-manual.html
@@ -0,0 +1,58 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Dispatch pointerleave (pen). </title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="When a pointing device that supports hover (pen stylus) leaves the range of the digitizer while over an element, the pointerleave event must be dispatched."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <!-- /resources/testharness.js -->
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointerleave event"); // set up test harness
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, "pointerleave", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ check_PointerEvent(event);
+ test_pointerEvent.step(function () {
+ assert_equals(event.pointerType, "pen", "Test should be run using a pen as input");
+ assert_equals(event.type, "pointerleave", "The " + event.type + " event was received");
+ assert_true((event.clientX > target0.getBoundingClientRect().left)&&
+ (event.clientX < target0.getBoundingClientRect().right)&&
+ (event.clientY > target0.getBoundingClientRect().top)&&
+ (event.clientY < target0.getBoundingClientRect().bottom),
+ "pointerleave should be received inside of target bounds");
+ });
+ test_pointerEvent.done(); // complete test
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Dispatch pointerleave (pen)</h1>
+ <h4>
+ Test Description:
+ When a pointing device that supports hover (pen stylus) leaves the range of the digitizer while over an element, the pointerleave event must be dispatched.
+ </h4>
+ <br />
+ <div id="target0">
+ Use a pen to hover over then lift up away from this element.
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointerout_after_pointercancel_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointerout_after_pointercancel_touch-manual.html
new file mode 100644
index 0000000000..1888591a7c
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_pointerout_after_pointercancel_touch-manual.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerout</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable" onload="run()">
+ <h2>pointerout</h2>
+ <h4>Test Description: This test checks if pointerout event triggers after pointercancel. Start touch on the black rectangle and move your touch to scroll in any direction. </h4>
+ <p>Note: this test is for touch devices only</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerout = async_test("pointerout event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var eventTested = false;
+ var pointercancel_event = null;
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ on_event(target0, "pointercancel", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ pointercancel_event = event;
+ });
+
+ // After firing the pointercancel event the pointerout event must be dispatched.
+ // TA: 4.3
+ on_event(target0, "pointerout", function (event) {
+ if(event.pointerType == 'touch') {
+ if(pointercancel_event != null) {
+ if (eventTested == false) {
+ test_pointerout.step(function() {
+ assert_equals(event.pointerType, pointercancel_event.pointerType, "pointerType is same for pointercancel and pointerout");
+ assert_equals(event.isPrimary, pointercancel_event.isPrimary, "isPrimary is same for pointercancel and pointerout");
+ });
+ eventTested = true;
+ test_pointerout.done();
+ }
+ }
+ else {
+ test_pointerout.step(function() {
+ assert_true(false,
+ "pointercancel received before pointerout");
+ }, "pointercancel received before pointerout");
+ }
+ }
+ });
+ }
+
+ </script>
+ <h1>Pointer Events pointerout tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_pointerout_pen-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_pointerout_pen-manual.html
new file mode 100644
index 0000000000..3973948c16
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_pointerout_pen-manual.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<html>
+ <head>
+ <title>pointerout</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ </head>
+ <body onload="run()">
+ <h2>pointerout</h2>
+ <h4>Test Description: This test checks if pointerout event triggers for pen. Place your pen over the black rectangle and then pull the pen out of the digitizer's detectable range. </h4>
+ <p>Note: this test is for devices that support hover - for pen only</p>
+ <div id="target0"></div>
+ <script>
+ var test_pointerout = async_test("pointerout event received");
+ // showPointerTypes is defined in pointerevent_support.js
+ // Requirements: the callback function will reference the test_pointerEvent object and
+ // will fail unless the async_test is created with the var name "test_pointerEvent".
+ add_completion_callback(showPointerTypes);
+
+ var eventTested = false;
+ var isPointerupReceived = false;
+ var detected_pointertypes = {};
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ // When a pen stylus leaves the hover range detectable by the digitizer the pointerout event must be dispatched.
+ // TA: 7.2
+ on_event(target0, "pointerout", function (event) {
+ detected_pointertypes[event.pointerType] = true;
+ if(event.pointerType == 'pen') {
+ if (eventTested == false) {
+ eventTested = true;
+ test_pointerout.done();
+ }
+ }
+ else {
+ test_pointerout.step(function() {
+ assert_true(false,
+ "you have to use pen for this test");
+ }, "you have to use pen for this test");
+ }
+ });
+ }
+
+ </script>
+ <h1>Pointer Events pointerout tests</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html
new file mode 100644
index 0000000000..3386fafb5a
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_events_to_original_target-manual.html
@@ -0,0 +1,137 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: releasePointerCapture() - subsequent events follow normal hitting testing mechanisms</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Microsoft" href="http://www.microsoft.com/"/>
+ <meta name="assert" content="After invoking the releasePointerCapture method on an element, subsequent events for the specified pointer must follow normal hit testing mechanisms for determining the event target"/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var test_pointerEvent;
+ var detected_pointertypes = {};
+ var captured_event = null;
+ var test_done = false;
+ var overEnterEventsFail = false;
+ var outLeaveEventsFail = false;
+ var f_gotPointerCapture = false;
+ var f_lostPointerCapture = false;
+
+ function resetTestState() {
+ captured_event = null;
+ test_done = false;
+ overEnterEventsFail = false;
+ outLeaveEventsFail = false;
+ f_gotPointerCapture = false;
+ f_lostPointerCapture = false;
+ }
+
+ function listenerEventHandler(event) {
+ if (test_done)
+ return;
+ detected_pointertypes[event.pointerType] = true;
+ if (event.type == "gotpointercapture") {
+ f_gotPointerCapture = true;
+ check_PointerEvent(event);
+ }
+ else if (event.type == "lostpointercapture") {
+ f_lostPointerCapture = true;
+ f_gotPointerCapture = false;
+ check_PointerEvent(event);
+ }
+ else if(event.type == "pointerover" || event.type == "pointerenter") {
+ if(captured_event && !overEnterEventsFail) {
+ test(function() {
+ assert_false(f_gotPointerCapture, "pointerover/enter should be received before the target receives gotpointercapture even when the pointer is not over it.");
+ }, expectedPointerType + " pointerover/enter should be received before the target receives gotpointercapture even when the pointer is not over it.");
+ overEnterEventsFail = true;
+ }
+ }
+ else if(event.type == "pointerout" || event.type == "pointerleave") {
+ if(!outLeaveEventsFail) {
+ test(function() {
+ assert_true(f_lostPointerCapture, "pointerout/leave should not be received unless the target just lost the capture.");
+ }, expectedPointerType + " pointerout/leave should not be received unless the target just lost the capture.");
+ outLeaveEventsFail = true;
+ }
+ }
+ else if (event.pointerId == captured_event.pointerId) {
+ if (f_gotPointerCapture && event.type == "pointermove") {
+ // on first event received for capture, release capture
+ listener.releasePointerCapture(event.pointerId);
+ }
+ else {
+ // if any other events are received after releaseCapture, then the test fails
+ test(function () {
+ assert_unreached(event.target.id + "-" + event.type + " should be handled by target element handler");
+ }, expectedPointerType + " No other events should be recieved by capturing node after release");
+ }
+ }
+ }
+
+ function targetEventHandler(event) {
+ if (test_done)
+ return;
+ if (f_gotPointerCapture) {
+ if(event.type != "pointerout" && event.type != "pointerleave") {
+ test(function () {
+ assert_unreached("The Target element should not have received any events while capture is active. Event recieved:" + event.type + ". ");
+ }, expectedPointerType + " The target element should not receive any events while capture is active");
+ }
+ }
+
+ if (event.type == "pointerdown") {
+ // pointerdown event received will be used to capture events.
+ listener.setPointerCapture(event.pointerId);
+ captured_event = event;
+ }
+
+ if (f_lostPointerCapture) {
+ test_pointerEvent.step(function () {
+ assert_equals(event.pointerId, captured_event.pointerId, "pointerID is same for event captured and after release");
+ });
+ if (event.type == "pointerup") {
+ test_done = true;
+ test_pointerEvent.done(); // complete test
+ }
+ }
+ }
+
+ function run() {
+ test_pointerEvent = setup_pointerevent_test("got/lost pointercapture: subsequent events to target", ALL_POINTERS); // set up test harness
+ var listener = document.getElementById("listener");
+ var target0 = document.getElementById("target0");
+ target0.style.touchAction = "none";
+
+ // target0 and listener - handle all events
+ for (var i = 0; i < All_Pointer_Events.length; i++) {
+ on_event(target0, All_Pointer_Events[i], targetEventHandler);
+ on_event(listener, All_Pointer_Events[i], listenerEventHandler);
+ }
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h2 id="pointerTypeDescription"></h2>
+ <div id="listener"></div>
+ <h1>Pointer Event: releasePointerCapture() - subsequent events follow normal hitting testing mechanisms</h1>
+ <h4>
+ Test Description:
+ Use your pointer and press down in the black box. Then move around in the box and release your pointer.
+ After invoking the releasePointerCapture method on an element, subsequent events for the specified
+ pointer must follow normal hit testing mechanisms for determining the event target.
+ </h4>
+ <br />
+ <div id="target0">
+ </div>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>Refresh the page to run the tests again with a different pointer type.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html
new file mode 100644
index 0000000000..105e3b5a97
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_releasepointercapture_onpointercancel_touch-manual.html
@@ -0,0 +1,71 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Release capture on pointercancel</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ </head>
+ <body class="scrollable">
+ <h1>Pointer Events Capture Test - release capture on pointercancel</h1>
+ <h4>
+ Test Description: This test checks if setCapture/releaseCapture functions works properly. Complete the following actions:
+ <ol>
+ <li> Touch black rectangle and do not release your touch
+ <li> Move your touch to scroll the page. "lostpointercapture" should be logged inside of the black rectangle immediately after "pointercancel"
+ </ol>
+ </h4>
+ Test passes if the proper behavior of the events is observed.
+ <div id="target0" style="background:black; color:white"></div>
+
+ <script type='text/javascript'>
+ var pointercancelGot = false;
+ var count=0;
+ var detected_pointertypes = {};
+ var test_pointerEvent = async_test("pointer capture is released on pointercancel");
+
+ var target0 = document.getElementById('target0');
+
+ add_completion_callback(showPointerTypes);
+
+ window.onload = function() {
+ on_event(target0, 'pointerdown', function(e) {
+ detected_pointertypes[e.pointerType] = true;
+ test_pointerEvent.step(function () {
+ assert_equals(e.pointerType, "touch", "Test should be run using a touch as input");
+ });
+ isPointerCapture = true;
+ sPointerCapture(e);
+ pointercancelGot = false;
+ });
+
+ on_event(target0, 'gotpointercapture', function(e) {
+ log("gotpointercapture", document.getElementById('target0'));
+ });
+
+ // If the setPointerCapture method has been invoked on the pointer specified by pointerId, and the releasePointerCapture method has not been invoked, a lostpointercapture event must be dispatched to the element on which the setPointerCapture method was invoked. Furthermore, subsequent events for the specified pointer must follow normal hit testing mechanisms for determining the event target.
+ // TA: 4.4
+ on_event(target0, 'lostpointercapture', function(e) {
+ log("lostpointercapture", document.getElementById('target0'));
+ test_pointerEvent.step(function () {
+ assert_true(pointercancelGot, "pointercancel was received before lostpointercapture");
+ });
+ test_pointerEvent.done();
+ });
+
+ on_event(target0, 'pointercancel', function(e) {
+ log("pointercancel", target0);
+ pointercancelGot = true;
+ });
+ }
+ </script>
+ <h1>Pointer Events Capture Test</h1>
+ <div id="complete-notice">
+ <p>Test complete: Scroll to Summary to view Pass/Fail Results.</p>
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_click-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_click-manual.html
new file mode 100644
index 0000000000..274f9a435b
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_click-manual.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Event sequence at implicit release on click</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a captured pointer is implicitly released after a click, the boundary events should follow the lostpointercapture event."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var event_log = [];
+ var start_logging = false;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ event_log = [];
+ start_logging = false;
+ }
+
+ function run() {
+ var test_pointer_event = setup_pointerevent_test("Event sequence at implicit release on click", ALL_POINTERS);
+
+ on_event(document.getElementById("done"), "click", function() {
+ test_pointer_event.step(function () {
+ var expected_events = "pointerup, lostpointercapture, pointerout, pointerleave";
+ assert_equals(event_log.join(", "), expected_events);
+ });
+ test_pointer_event.done();
+ });
+
+ var target = document.getElementById("target");
+
+ All_Pointer_Events.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (event.type == "pointerdown") {
+ event.target.setPointerCapture(event.pointerId);
+
+ } else if (event.type == "gotpointercapture") {
+ start_logging = true;
+
+ } else if (event.type != "pointermove" && start_logging) {
+ event_log.push(event.type);
+ }
+ });
+ });
+ }
+ </script>
+ <style>
+ #target {
+ margin: 20px;
+ background-color: black;
+ }
+
+ #done {
+ margin: 20px;
+ background-color: green;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Event sequence at implicit release on click<h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ When a captured pointer is implicitly released after a click, the boundary events should follow the lostpointercapture event.
+ </h4>
+ <ol>
+ <li>Click or tap on Black.</li>
+ <li>Click or tap on Green.</li>
+ </ol>
+ <div id="target"></div>
+ <div id="done"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_drag-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_drag-manual.html
new file mode 100644
index 0000000000..7b8e39b94d
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_sequence_at_implicit_release_on_drag-manual.html
@@ -0,0 +1,84 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Event: Event sequence at implicit release on drag</title>
+ <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
+ <link rel="author" title="Google" href="http://www.google.com "/>
+ <meta name="assert" content="When a captured pointer is implicitly released after a drag, the boundary events should follow the lostpointercapture event."/>
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script type="text/javascript" src="pointerevent_support.js"></script>
+ <script type="text/javascript">
+ var detected_pointertypes = {};
+ var event_log = [];
+ var start_logging = false;
+
+ function resetTestState() {
+ detected_eventTypes = {};
+ event_log = [];
+ start_logging = false;
+ }
+
+ function run() {
+ var test_pointer_event = setup_pointerevent_test("Event sequence at implicit release on drag", ["touch"]);
+
+ on_event(document.getElementById("done"), "click", function() {
+ test_pointer_event.step(function () {
+ var expected_events = "pointercancel, lostpointercapture, pointerout, pointerleave";
+ assert_equals(event_log.join(", "), expected_events);
+ });
+ test_pointer_event.done();
+ });
+
+ var target = document.getElementById("target");
+
+ All_Pointer_Events.forEach(function(eventName) {
+ on_event(target, eventName, function (event) {
+ detected_pointertypes[event.pointerType] = true;
+
+ if (event.type == "pointerdown") {
+ event.target.setPointerCapture(event.pointerId);
+
+ } else if (event.type == "gotpointercapture") {
+ start_logging = true;
+
+ } else if (event.type != "pointermove" && start_logging) {
+ event_log.push(event.type);
+ }
+ });
+ });
+ }
+ </script>
+ <style>
+ #target {
+ margin: 20px;
+ background-color: black;
+ touch-action: auto;
+ }
+
+ #done {
+ margin: 20px;
+ background-color: green;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Event: Event sequence at implicit release on drag<h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ When a captured pointer is implicitly released after a drag, the boundary events should follow the lostpointercapture event.
+ </h4>
+ <ol>
+ <li>Drag quickly down starting on Black.</li>
+ <li>Click or tap on Green.</li>
+ </ol>
+ <div id="target"></div>
+ <div id="done"></div>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ <p>The following events were logged: <span id="event-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_styles.css b/dom/events/test/pointerevents/wpt/pointerevent_styles.css
new file mode 100644
index 0000000000..1ee3b0b396
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_styles.css
@@ -0,0 +1,112 @@
+#innerFrame {
+position: absolute;
+top: 300px;
+left: 200px;
+height: 100px;
+width: 100px;
+}
+
+.spacer {
+height: 100px;
+}
+
+#square1 {
+top: 330px;
+left: 150px;
+background: black;
+}
+
+#square2 {
+top: 50px;
+left: 30px;
+visibility: hidden;
+background: red;
+}
+
+.square {
+height: 20px;
+width: 20px;
+position: absolute;
+padding: 0px;
+}
+
+#target0 {
+background: black;
+color: white;
+white-space: nowrap;
+overflow-y: auto;
+overflow-x: auto;
+}
+
+#target1 {
+background: purple;
+color: white;
+white-space: nowrap;
+overflow-y: auto;
+overflow-x: auto;
+}
+
+#scrollTarget {
+ background: darkblue;
+}
+
+.touchActionNone {
+touch-action: none;
+}
+
+#innerframe {
+width: 90%;
+margin: 10px;
+margin-left: 10%;
+height: 200px;
+}
+
+.scroller {
+width: 700px;
+height: 430px;
+margin: 20px;
+overflow: auto;
+background: black;
+}
+
+.scroller > div {
+height: 1000px;
+width: 1000px;
+color: white;
+}
+
+.scroller > div div {
+height: 100%;
+width: 100%;
+color: white;
+}
+
+div {
+margin: 0em;
+padding: 2em;
+}
+
+#complete-notice {
+background: #afa;
+border: 1px solid #0a0;
+display: none;
+}
+
+#pointertype-log {
+font-weight: bold;
+}
+
+#event-log {
+font-weight: bold;
+}
+
+#listener {
+background: orange;
+border: 1px solid orange;
+position: absolute;
+top: -100px;
+}
+
+body.scrollable {
+min-height: 5000px;
+}
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_support.js b/dom/events/test/pointerevents/wpt/pointerevent_support.js
new file mode 100644
index 0000000000..cd3c799f73
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_support.js
@@ -0,0 +1,333 @@
+var All_Pointer_Events = [
+ "pointerdown",
+ "pointerup",
+ "pointercancel",
+ "pointermove",
+ "pointerover",
+ "pointerout",
+ "pointerenter",
+ "pointerleave",
+ "gotpointercapture",
+ "lostpointercapture",
+];
+
+// Check for conformance to PointerEvent interface
+// TA: 1.1, 1.2, 1.6, 1.7, 1.8, 1.9, 1.10, 1.11, 1.12, 1.13
+function check_PointerEvent(event, testNamePrefix) {
+ if (testNamePrefix === undefined) {
+ testNamePrefix = "";
+ }
+
+ // Use expectedPointerType if set otherwise just use the incoming event pointerType in the test name.
+ var pointerTestName =
+ testNamePrefix +
+ " " +
+ (expectedPointerType == null ? event.pointerType : expectedPointerType) +
+ " " +
+ event.type;
+
+ if (expectedPointerType != null) {
+ test(function () {
+ assert_equals(
+ event.pointerType,
+ expectedPointerType,
+ "pointerType should be the one specified in the test page."
+ );
+ }, pointerTestName + " event pointerType is correct.");
+ }
+
+ test(function () {
+ assert_true(
+ event instanceof event.target.ownerDocument.defaultView.PointerEvent,
+ "event is a PointerEvent event"
+ );
+ }, pointerTestName + " event is a PointerEvent event");
+
+ // Check attributes for conformance to WebIDL:
+ // * attribute exists
+ // * has proper type
+ // * if the attribute is "readonly", it cannot be changed
+ // TA: 1.1, 1.2
+ var idl_type_check = {
+ long(v) {
+ return typeof v === "number" && Math.round(v) === v;
+ },
+ float(v) {
+ return typeof v === "number";
+ },
+ string(v) {
+ return typeof v === "string";
+ },
+ boolean(v) {
+ return typeof v === "boolean";
+ },
+ };
+ [
+ ["readonly", "long", "pointerId"],
+ ["readonly", "float", "width"],
+ ["readonly", "float", "height"],
+ ["readonly", "float", "pressure"],
+ ["readonly", "long", "tiltX"],
+ ["readonly", "long", "tiltY"],
+ ["readonly", "string", "pointerType"],
+ ["readonly", "boolean", "isPrimary"],
+ ["readonly", "long", "detail", 0],
+ ].forEach(function (attr) {
+ var readonly = attr[0];
+ var type = attr[1];
+ var name = attr[2];
+ var value = attr[3];
+
+ // existence check
+ test(function () {
+ assert_true(
+ name in event,
+ name + " attribute in " + event.type + " event"
+ );
+ }, pointerTestName + "." + name + " attribute exists");
+
+ // readonly check
+ if (readonly === "readonly") {
+ test(function () {
+ assert_readonly(
+ event.type,
+ name,
+ event.type + "." + name + " cannot be changed"
+ );
+ }, pointerTestName + "." + name + " is readonly");
+ }
+
+ // type check
+ test(function () {
+ assert_true(
+ idl_type_check[type](event[name]),
+ name + " attribute of type " + type
+ );
+ }, pointerTestName +
+ "." +
+ name +
+ " IDL type " +
+ type +
+ " (JS type was " +
+ typeof event[name] +
+ ")");
+
+ // value check if defined
+ if (value != undefined) {
+ test(function () {
+ assert_equals(event[name], value, name + " attribute value");
+ }, pointerTestName + "." + name + " value is " + value + ".");
+ }
+ });
+
+ // Check the pressure value
+ // TA: 1.6, 1.7, 1.8
+ test(function () {
+ // TA: 1.6
+ assert_greater_than_equal(
+ event.pressure,
+ 0,
+ "pressure is greater than or equal to 0"
+ );
+ assert_less_than_equal(
+ event.pressure,
+ 1,
+ "pressure is less than or equal to 1"
+ );
+
+ if (event.type === "pointerup") {
+ assert_equals(event.pressure, 0, "pressure is 0 during pointerup");
+ }
+
+ // TA: 1.7, 1.8
+ if (event.pointerType === "mouse") {
+ if (event.buttons === 0) {
+ assert_equals(
+ event.pressure,
+ 0,
+ "pressure is 0 for mouse with no buttons pressed"
+ );
+ } else {
+ assert_equals(
+ event.pressure,
+ 0.5,
+ "pressure is 0.5 for mouse with a button pressed"
+ );
+ }
+ }
+ }, pointerTestName + ".pressure value is valid");
+
+ // Check mouse-specific properties
+ if (event.pointerType === "mouse") {
+ // TA: 1.9, 1.10, 1.13
+ test(function () {
+ assert_equals(event.width, 1, "width of mouse should be 1");
+ assert_equals(event.height, 1, "height of mouse should be 1");
+ assert_equals(event.tiltX, 0, event.type + ".tiltX is 0 for mouse");
+ assert_equals(event.tiltY, 0, event.type + ".tiltY is 0 for mouse");
+ assert_true(event.isPrimary, event.type + ".isPrimary is true for mouse");
+ }, pointerTestName + " properties for pointerType = mouse");
+ // Check properties for pointers other than mouse
+ }
+}
+
+function showPointerTypes() {
+ var complete_notice = document.getElementById("complete-notice");
+ var pointertype_log = document.getElementById("pointertype-log");
+ var pointertypes = Object.keys(detected_pointertypes);
+ pointertype_log.innerHTML = pointertypes.length
+ ? pointertypes.join(",")
+ : "(none)";
+ complete_notice.style.display = "block";
+}
+
+function showLoggedEvents() {
+ var event_log_elem = document.getElementById("event-log");
+ event_log_elem.innerHTML = event_log.length ? event_log.join(", ") : "(none)";
+
+ var complete_notice = document.getElementById("complete-notice");
+ complete_notice.style.display = "block";
+}
+
+function log(msg, el) {
+ if (++count > 10) {
+ count = 0;
+ el.innerHTML = " ";
+ }
+ el.innerHTML = msg + "; " + el.innerHTML;
+}
+
+function failOnScroll() {
+ assert_true(false, "scroll received while shouldn't");
+}
+
+function updateDescriptionNextStep() {
+ document.getElementById("desc").innerHTML =
+ "Test Description: Try to scroll text RIGHT.";
+}
+
+function updateDescriptionComplete() {
+ document.getElementById("desc").innerHTML = "Test Description: Test complete";
+}
+
+function updateDescriptionSecondStepTouchActionElement(
+ target,
+ scrollReturnInterval
+) {
+ window.setTimeout(function () {
+ objectScroller(target, "up", 0);
+ }, scrollReturnInterval);
+ document.getElementById("desc").innerHTML =
+ "Test Description: Try to scroll element RIGHT moving your outside of the red border";
+}
+
+function updateDescriptionThirdStepTouchActionElement(
+ target,
+ scrollReturnInterval,
+ callback = null
+) {
+ window.setTimeout(function () {
+ objectScroller(target, "left", 0);
+ if (callback) {
+ callback();
+ }
+ }, scrollReturnInterval);
+ document.getElementById("desc").innerHTML =
+ "Test Description: Try to scroll element DOWN then RIGHT starting your touch inside of the element. Then tap complete button";
+}
+
+function updateDescriptionFourthStepTouchActionElement(
+ target,
+ scrollReturnInterval
+) {
+ document.getElementById("desc").innerHTML =
+ "Test Description: Try to scroll element RIGHT starting your touch inside of the element";
+}
+
+function objectScroller(target, direction, value) {
+ if (direction == "up") {
+ target.scrollTop = 0;
+ } else if (direction == "left") {
+ target.scrollLeft = 0;
+ }
+}
+
+function sPointerCapture(e) {
+ try {
+ target0.setPointerCapture(e.pointerId);
+ } catch (ex) {}
+}
+
+function rPointerCapture(e) {
+ try {
+ captureButton.value = "Set Capture";
+ target0.releasePointerCapture(e.pointerId);
+ } catch (ex) {}
+}
+
+var globalPointerEventTest = null;
+var expectedPointerType = null;
+const ALL_POINTERS = ["mouse", "touch", "pen"];
+const HOVERABLE_POINTERS = ["mouse", "pen"];
+const NOHOVER_POINTERS = ["touch"];
+
+function MultiPointerTypeTest(testName, types) {
+ this.testName = testName;
+ this.types = types;
+ this.currentTypeIndex = 0;
+ this.currentTest = null;
+ this.createNextTest();
+}
+
+MultiPointerTypeTest.prototype.skip = function () {
+ var prevTest = this.currentTest;
+ this.createNextTest();
+ prevTest.timeout();
+};
+
+MultiPointerTypeTest.prototype.done = function () {
+ var prevTest = this.currentTest;
+ this.createNextTest();
+ if (prevTest != null) {
+ prevTest.done();
+ }
+};
+
+MultiPointerTypeTest.prototype.step = function (stepFunction) {
+ this.currentTest.step(stepFunction);
+};
+
+MultiPointerTypeTest.prototype.createNextTest = function () {
+ if (this.currentTypeIndex < this.types.length) {
+ var pointerTypeDescription = document.getElementById(
+ "pointerTypeDescription"
+ );
+ document.getElementById("pointerTypeDescription").innerHTML =
+ "Follow the test instructions with <span style='color: red'>" +
+ this.types[this.currentTypeIndex] +
+ "</span>. If you don't have the device <a href='javascript:;' onclick='globalPointerEventTest.skip()'>skip it</a>.";
+ this.currentTest = async_test(
+ this.types[this.currentTypeIndex] + " " + this.testName
+ );
+ expectedPointerType = this.types[this.currentTypeIndex];
+ this.currentTypeIndex++;
+ } else {
+ document.getElementById("pointerTypeDescription").innerHTML = "";
+ }
+ resetTestState();
+};
+
+function setup_pointerevent_test(testName, supportedPointerTypes) {
+ return (globalPointerEventTest = new MultiPointerTypeTest(
+ testName,
+ supportedPointerTypes
+ ));
+}
+
+function checkPointerEventType(event) {
+ assert_equals(
+ event.pointerType,
+ expectedPointerType,
+ "pointerType should be the same as the requested device."
+ );
+}
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-auto-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-auto-css_touch-manual.html
new file mode 100644
index 0000000000..f5e9d12c35
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-auto-css_touch-manual.html
@@ -0,0 +1,129 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: auto</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: auto;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text DOWN. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(event.pointerType, "touch", "wrong pointer type was detected: ");
+ });
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStep();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction.done();
+ updateDescriptionComplete();
+ }
+ });
+ }
+ </script>
+ <h1>touch-action: auto</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-button-test_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-button-test_touch-manual.html
new file mode 100644
index 0000000000..c7c5d9a440
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-button-test_touch-manual.html
@@ -0,0 +1,110 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Button touch-action test</title>
+ <meta name="assert" content="TA15.11 -The touch-action CSS property applies to button elements.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 150px;
+ width: 200px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ button {
+ touch-action: none;
+ width: 350px;
+ height: 350px;
+ border: 2px solid red;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll black element DOWN moving your touch outside of the red border. Wait for description update.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <button id="testButton">Test Button</button>
+ </div>
+ <br>
+ <input type="button" id="btnComplete" value="Complete test">
+
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+ var scrollReturnInterval = 1000;
+ var isFirstPart = true;
+ setup({ explicit_timeout: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ //TA 15.11
+ var test_touchaction_div = async_test("touch-action attribute test out of element");
+ var test_touchaction_button = async_test("touch-action attribute test in element");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction_button.step(function() {
+ assert_equals(target0.scrollLeft, 0, "button scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "button scroll y offset should be 0 in the end of the test");
+ assert_true(xScrollIsReceived && yScrollIsReceived, "target0 x and y scroll offsets should be greater than 0 after first two interactions (outside red border) respectively");
+ });
+ test_touchaction_button.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ if(isFirstPart) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction_div.step(function () {
+ yScrollIsReceived = true;
+ });
+ updateDescriptionSecondStepTouchActionElement(target0, scrollReturnInterval);
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction_div.done();
+ updateDescriptionThirdStepTouchActionElement(target0, scrollReturnInterval, function () {
+ setTimeout(function() {
+ isFirstPart = false;
+ }, scrollReturnInterval); // avoid immediate triggering while scroll is still being performed
+ });
+ }
+ }
+ else {
+ test_touchaction_button.step(failOnScroll, "scroll received while shouldn't");
+ }
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-illegal.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-illegal.html
new file mode 100644
index 0000000000..5fe6179840
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-illegal.html
@@ -0,0 +1,67 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: illegal</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 50px;
+ touch-action: pan-x none;
+ }
+ #target1 {
+ width: 700px;
+ height: 50px;
+ background: black;
+ margin-top: 5px;
+ touch-action: pan-y none;
+ }
+ #target2 {
+ width: 700px;
+ height: 50px;
+ background: black;
+ margin-top: 5px;
+ touch-action: auto none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Test will automatically check behaviour of following combinations: 'pan-x none', 'pan-y none', 'auto none'</h4>
+ <div id="target0"></div>
+ <div id="target1"></div>
+ <div id="target2"></div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ setup({ explicit_done: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById('target0');
+ var target1 = document.getElementById('target1');
+ var target2 = document.getElementById('target2');
+
+ test(function() {
+ assert_true(getComputedStyle(target0).touchAction == 'auto', "'pan-x none' is corrected properly");
+ }, "'pan-x none' is corrected properly");
+ test(function() {
+ assert_true(getComputedStyle(target1).touchAction == 'auto', "'pan-y none' is corrected properly");
+ }, "'pan-y none' is corrected properly");
+ test(function() {
+ assert_true(getComputedStyle(target2).touchAction == 'auto', "'auto none' is corrected properly");
+ }, "'auto none' is corrected properly");
+ done();
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
new file mode 100644
index 0000000000..dcea283750
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-auto-child-none_touch-manual.html
@@ -0,0 +1,117 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: auto > child: none</title>
+ <meta name="assert" content="TA15.5 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `auto`, Grand-Child: `none`">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: auto;
+ }
+ .scroller > div div {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if touch-action attribute works properly for embedded divs
+ // Scrollable-Parent, Child: `auto`, Grand-Child: `none`
+ // TA: 15.5
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-none_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-none_touch-manual.html
new file mode 100644
index 0000000000..16e42954e5
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-none_touch-manual.html
@@ -0,0 +1,112 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: child: none</title>
+ <meta name="assert" content="TA15.9 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `none`">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.9
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
new file mode 100644
index 0000000000..c75d067e44
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-x_touch-manual.html
@@ -0,0 +1,112 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: pan-x > child: pan-x</title>
+ <meta name="assert" content="TA15.6 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent, Child: `pan-x`, Grand-Child: `pan-x`">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: pan-x;
+ }
+ .scroller > div div {
+ touch-action: pan-x;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: only pans in x direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.6
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_not_equals(target0.scrollLeft, 0, "scroll x offset should not be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>behaviour: pan-x</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
new file mode 100644
index 0000000000..d420cc56c7
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_child-pan-x-child-pan-y_touch-manual.html
@@ -0,0 +1,117 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: pan-x > child: pan-y</title>
+ <meta name="assert" content="TA15.13 - Touch action inherits child 'pan-x' -> child 'pan-y' test">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: pan-x;
+ }
+ .scroller > div div {
+ touch-action: pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning/zooming/etc.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if touch-action attribute works properly for embedded divs
+ // Scrollable-Parent, Child: `pan-x`, Grand-Child: `pan-y`
+ // TA: 15.13
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
new file mode 100644
index 0000000000..d87d2b3a34
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_highest-parent-none_touch-manual.html
@@ -0,0 +1,133 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent: none + two embedded children</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #divParent {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text DOWN inside blue rectangle. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="divParent">
+ <div class="scroller" id="target0">
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+ add_completion_callback(enableScrolling);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ yScrollIsReceived = true;
+ updateDescriptionNextStep();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction.done();
+ updateDescriptionComplete();
+ }
+ });
+ }
+
+ function enableScrolling() {
+ document.getElementById('divParent').setAttribute('style', 'touch-action: auto');
+ }
+ </script>
+ <h1>behaviour: auto</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_parent-none_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_parent-none_touch-manual.html
new file mode 100644
index 0000000000..5e674a14da
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-inherit_parent-none_touch-manual.html
@@ -0,0 +1,112 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: inherit from parent: none</title>
+ <meta name="assert" content="TA15.8 - when a user touches an element, the effect of that touch is determined by the value of the touch-action property and the default touch behaviors on the element and its ancestors. Scrollable-Parent: `none` Child: `auto`">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: no panning</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div id="scrollTarget">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.8
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+ }
+ </script>
+ <h1>behaviour: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-keyboard-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-keyboard-manual.html
new file mode 100644
index 0000000000..3fef3f646f
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-keyboard-manual.html
@@ -0,0 +1,124 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: keyboard</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Press DOWN ARROW key. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for keyboard only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ target0.focus();
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStepKeyboard();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction.done();
+ updateDescriptionComplete();
+ }
+ });
+ }
+
+ function updateDescriptionNextStepKeyboard() {
+ document.getElementById('desc').innerHTML = "Test Description: press RIGHT ARROW key.";
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-mouse-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-mouse-manual.html
new file mode 100644
index 0000000000..fcc8584515
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-mouse-manual.html
@@ -0,0 +1,130 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: mouse</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text down using mouse (use mouse wheel or click on the scrollbar). Wait for description update.</h4>
+ <p>Note: this test is for mouse only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStepMouse();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction.done();
+ updateDescriptionComplete();
+ }
+ });
+ }
+
+ function updateDescriptionNextStepMouse() {
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll text right using mouse (use mouse wheel or click on the scrollbar).";
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-none-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-none-css_touch-manual.html
new file mode 100644
index 0000000000..dec694f3ec
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-none-css_touch-manual.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: none</title>
+ <meta name="assert" content="TA15.2 - With `touch-action: none` on a swiped or click/dragged element, `pointerdown+(optional pointermove)+pointerup` must be dispatched.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: no panning/zooming/etc.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if "touch-action: none" attribute works properly
+ //TA: 15.2
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ test_touchaction.step(failOnScroll, "scroll received while touch-action is none");
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-down-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-down-css_touch-manual.html
new file mode 100644
index 0000000000..16e1cb2fab
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-down-css_touch-manual.html
@@ -0,0 +1,114 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-down</title>
+ <meta name="assert" content="TA15.4 - With `touch-action: pan-down` on a swiped or click/dragged element, only panning in the y-axis down direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-down;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element UP (drag down), then RIGHT (drag left), then DOWN (drag up). Tap Complete button under the rectangle when done. Expected: only pans in down direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ target0.scrollTop = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_greater_than_equal(target0.scrollTop, 200);
+ });
+
+ // Check if "touch-action: pan-down" attribute works properly
+ //TA: 15.4
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_greater_than(target0.scrollTop, 200, "scroll y offset should be greater than 200 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>touch-action: pan-down</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-left-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-left-css_touch-manual.html
new file mode 100644
index 0000000000..53fd2de138
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-left-css_touch-manual.html
@@ -0,0 +1,114 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-left</title>
+ <meta name="assert" content="TA15.3 - With `touch-action: pan-left` on a swiped or click/dragged element, only panning on the x-axis left direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-left;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN (drag up), then RIGHT (drag left), then LEFT (drag right). Tap Complete button under the rectangle when done. Expected: only pans in left direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ target0.scrollLeft = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_less_than_equal(target0.scrollLeft, 200);
+ });
+
+ // Check if "touch-action: pan-left" attribute works properly
+ //TA: 15.3
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_less_than(target0.scrollLeft, 200, "scroll x offset should be less than 200 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>touch-action: pan-left</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-right-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-right-css_touch-manual.html
new file mode 100644
index 0000000000..53bbac65ec
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-right-css_touch-manual.html
@@ -0,0 +1,114 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-right</title>
+ <meta name="assert" content="TA15.3 - With `touch-action: pan-right` on a swiped or click/dragged element, only panning on the x-axis right direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-right;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN (drag up), then LEFT (drag right), then RIGHT (drag left). Tap Complete button under the rectangle when done. Expected: only pans in right direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ target0.scrollLeft = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_greater_than_equal(target0.scrollLeft, 200);
+ });
+
+ // Check if "touch-action: pan-right" attribute works properly
+ //TA: 15.3
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_greater_than(target0.scrollLeft, 200, "scroll x offset should be greater than 200 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>touch-action: pan-right</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-up-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-up-css_touch-manual.html
new file mode 100644
index 0000000000..0902700d2d
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-up-css_touch-manual.html
@@ -0,0 +1,114 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-up</title>
+ <meta name="assert" content="TA15.4 - With `touch-action: pan-up` on a swiped or click/dragged element, only panning in the y-axis up direction should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-up;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN (drag up), then RIGHT (drag left), then UP (drag down). Tap Complete button under the rectangle when done. Expected: only pans in up direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+ target0.scrollTop = 200;
+
+ var scrollListenerExecuted = false;
+ target0.addEventListener("scroll", function(event) {
+ scrollListenerExecuted = true;
+ assert_less_than_equal(target0.scrollTop, 200);
+ });
+
+ // Check if "touch-action: pan-up" attribute works properly
+ //TA: 15.4
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_true(scrollListenerExecuted, "scroll listener should have been executed by the end of the test");
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_less_than(target0.scrollTop, 200, "scroll y offset should be less than 200 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>touch-action: pan-up</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-css_touch-manual.html
new file mode 100644
index 0000000000..e757baec6b
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-css_touch-manual.html
@@ -0,0 +1,106 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-x</title>
+ <meta name="assert" content="TA15.3 - With `touch-action: pan-x` on a swiped or click/dragged element, only panning on the x-axis should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-x;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: only pans in x direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if "touch-action: pan-x" attribute works properly
+ //TA: 15.3
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_not_equals(target0.scrollLeft, 0, "scroll x offset should not be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>touch-action: pan-x</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
new file mode 100644
index 0000000000..e2a4386b27
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y-pan-y_touch-manual.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: parent > child: pan-x pan-y > child: pan-y</title>
+ <meta name="assert" content="TA15.17 - Touch action 'pan-x pan-y' 'pan-y' test">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ .scroller > div {
+ touch-action: pan-x pan-y;
+ }
+ .scroller > div div {
+ touch-action: pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT inside blue rectangle. Tap Complete button under the rectangle when done. Expected: only pans in y direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div class="scroller" id="target0">
+ <div>
+ <div id="scrollTarget">
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ </div>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ add_completion_callback(showPointerTypes);
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if touch-action attribute works properly for embedded divs
+ //
+ // TA: 15.17
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_not_equals(target0.scrollTop, 0, "scroll y offset should not be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>behaviour: pan-y</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y_touch-manual.html
new file mode 100644
index 0000000000..0c900ff740
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-x-pan-y_touch-manual.html
@@ -0,0 +1,126 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-x pan-y</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-x pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll text DOWN. Wait for description update. Expected: pan enabled</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+
+ var test_touchaction = async_test("touch-action attribute test");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(target0, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionNextStep();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction.done();
+ updateDescriptionComplete();
+ }
+ });
+ }
+ </script>
+ <h1>touch-action: pan-x pan-y</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-y-css_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-y-css_touch-manual.html
new file mode 100644
index 0000000000..4ad39ecc83
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-pan-y-css_touch-manual.html
@@ -0,0 +1,106 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: pan-y</title>
+ <meta name="assert" content="TA15.4 - With `touch-action: pan-y` on a swiped or click/dragged element, only panning in the y-axis should be possible.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ width: 700px;
+ height: 430px;
+ touch-action: pan-y;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events touch-action attribute support</h1>
+ <h4 id="desc">Test Description: Try to scroll element DOWN then RIGHT. Tap Complete button under the rectangle when done. Expected: only pans in y direction.</h4>
+ <p>Note: this test is for touch-devices only</p>
+ <div id="target0">
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>
+ Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diem
+ nonummy nibh euismod tincidunt ut lacreet dolore magna aliguam erat volutpat.
+ Ut wisis enim ad minim veniam, quis nostrud exerci tution ullamcorper suscipit
+ lobortis nisl ut aliquip ex ea commodo consequat.
+ </p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ <p>Lorem ipsum dolor sit amet...</p>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var test_touchaction = async_test("touch-action attribute test");
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ // Check if "touch-action: pan-y" attribute works properly
+ //TA: 15.4
+ on_event(btnComplete, 'click', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ test_touchaction.step(function() {
+ assert_equals(target0.scrollLeft, 0, "scroll x offset should be 0 in the end of the test");
+ assert_not_equals(target0.scrollTop, 0, "scroll y offset should not be 0 in the end of the test");
+ });
+ test_touchaction.done();
+ updateDescriptionComplete();
+ });
+ }
+ </script>
+ <h1>touch-action: pan-y</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-span-test_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-span-test_touch-manual.html
new file mode 100644
index 0000000000..61f0e8d329
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-span-test_touch-manual.html
@@ -0,0 +1,114 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Span touch-action test</title>
+ <meta name="assert" content="TA15.18 - The touch-action CSS property applies to all elements except non-replaced inline elements."
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 150px;
+ width: 200px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ #testspan {
+ touch-action: none;
+ font-size: 72pt;
+ padding: 0px 0px 180px 0px;
+ border: 2px solid red;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll black element DOWN moving your touch outside of the red border. Wait for description update.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <span id="testspan">
+ Test span
+ </span>
+ </div>
+ <input type="button" id="btnComplete" value="Complete test">
+
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var failScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+ var scrollReturnInterval = 500;
+ var isFirstPart = true;
+ setup({ explicit_timeout: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ //TA 15.18
+ var test_touchaction_div = async_test("touch-action attribute test out of element");
+ var test_touchaction_span = async_test("touch-action attribute test in element");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction_span.step(function() {
+ assert_not_equals(target0.scrollLeft, 0, "span scroll x offset should not be 0 in the end of the test");
+ assert_not_equals(target0.scrollTop, 0, "span scroll y offset should not be 0 in the end of the test");
+ assert_true(!isFirstPart, "target0 x and y scroll offsets should be greater than 0 after first two interactions (outside red border) respectively");
+ });
+ test_touchaction_span.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ if(isFirstPart) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction_div.step(function () {
+ yScrollIsReceived = true;
+ });
+ updateDescriptionSecondStepTouchActionElement(target0, scrollReturnInterval);
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction_div.done();
+ updateDescriptionThirdStepTouchActionElement(target0, scrollReturnInterval, function () {
+ setTimeout(function() {
+ isFirstPart = false;
+ xScr0 = target0.scrollLeft;
+ xScr0 = target0.scrollLeft;
+ xScrollIsReceived = false;
+ yScrollIsReceived = false;
+ }, scrollReturnInterval); // avoid immediate triggering while scroll is still being performed
+ });
+ }
+ }
+ });
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html> \ No newline at end of file
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-svg-test_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-svg-test_touch-manual.html
new file mode 100644
index 0000000000..e9dc9d78ee
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-svg-test_touch-manual.html
@@ -0,0 +1,122 @@
+<!doctype html>
+<html>
+ <head>
+ <title>SVG test</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 350px;
+ width: 300px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll black element DOWN moving your touch outside of the red border. Wait for description update.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <svg id="testSvg" width="555" height="555" style="touch-action: none; border: 4px double red;">
+ <circle cx="305" cy="305" r="250" stroke="green" stroke-width="4" fill="yellow" />
+ Sorry, your browser does not support inline SVG.
+ </svg>
+ </div>
+ <br>
+ <input type="button" id="btnComplete" value="Complete test">
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+ var scrollReturnInterval = 1000;
+ var isFirstPart = true;
+ setup({ explicit_timeout: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ var test_touchaction_div = async_test("touch-action attribute test out of SVG");
+ var test_touchaction_svg = async_test("touch-action attribute test in SVG");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction_svg.step(function() {
+ assert_equals(target0.scrollLeft, 0, "SVG scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "SVG scroll y offset should be 0 in the end of the test");
+ });
+ test_touchaction_svg.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ if(isFirstPart) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction_div.step(function () {
+ yScrollIsReceived = true;
+ assert_true(true, "y-scroll received.");
+ });
+ updateDescriptionSecondStepSVG();
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction_div.done();
+ updateDescriptionThirdStepSVG();
+ setTimeout(function() {
+ isFirstPart = false;
+ }, 2 * scrollReturnInterval);
+ }
+ }
+ });
+ }
+
+ function updateDescriptionSecondStepSVG() {
+ window.setTimeout(function() {
+ objectScroller(target0, 'up', 0);}
+ , scrollReturnInterval);
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element RIGHT moving your touch outside of the red border";
+ }
+
+ function updateDescriptionThirdStepSVG() {
+ window.setTimeout(function() {
+ objectScroller(target0, 'left', 0);}
+ , scrollReturnInterval);
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element DOWN then RIGHT starting your touch inside of the circle. Tap Complete button under the rectangle when done";
+ }
+
+ function objectScroller(target, direction, value) {
+ if (direction == 'up') {
+ target.scrollTop = 0;
+ } else if (direction == 'left') {
+ target.scrollLeft = 0;
+ }
+ }
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-table-test_touch-manual.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-table-test_touch-manual.html
new file mode 100644
index 0000000000..17d5a29575
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-table-test_touch-manual.html
@@ -0,0 +1,145 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Table touch-action test</title>
+ <meta name="assert" content="TA15.19 The touch-action CSS property applies to all elements except table rows, row groups, table columns, and column groups.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ #target0 {
+ height: 150px;
+ width: 200px;
+ overflow-y: auto;
+ background: black;
+ padding: 100px;
+ position: relative;
+ }
+ #testtable{
+ color: white;
+ width: 350px;
+ padding: 0px 0px 200px 0px;
+ border: 2px solid green;
+ }
+ .testtd, .testth {
+ border: 2px solid green;
+ height: 80px;
+ }
+ #row1 {
+ touch-action: none;
+ }
+ #cell3 {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Try to scroll element DOWN starting your touch over the 1st Row. Wait for description update.</h4>
+ <p>Note: this test is for touch only</p>
+ <div id="target0">
+ <table id="testtable">
+ <caption>The caption, first row element, and cell 3 have touch-action: none.</caption>
+ <tr id="row1"><th class="testth">Header 1 <td class="testtd">Cell 1 <td class="testtd">Cell 2</tr>
+ <tr id="row2"><th class="testth">Header 2 <td id="cell3" class="testtd">Cell 3 <td class="testtd">Cell 4</tr>
+ <tr id="row3"> <th class="testth">Header 3 <td class="testtd">Cell 5 <td class="testtd"> Cell 6</tr>
+ </table>
+ </div>
+ <br>
+ <input type="button" id="btnComplete" value="Complete test">
+
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+ var xScrollIsReceived = false;
+ var yScrollIsReceived = false;
+ var xScr0, yScr0, xScr1, yScr1;
+ var scrollReturnInterval = 1000;
+ var isFirstPart = true;
+ setup({ explicit_timeout: true });
+ add_completion_callback(showPointerTypes);
+
+ function run() {
+ var target0 = document.getElementById("target0");
+ var btnComplete = document.getElementById("btnComplete");
+
+ //TA 15.19
+ var test_touchaction_cell = async_test("touch-action attribute test on the cell");
+ var test_touchaction_row = async_test("touch-action attribute test on the row");
+
+ xScr0 = target0.scrollLeft;
+ yScr0 = target0.scrollTop;
+
+ on_event(btnComplete, 'click', function(event) {
+ test_touchaction_cell.step(function() {
+ assert_equals(target0.scrollLeft, 0, "table scroll x offset should be 0 in the end of the test");
+ assert_equals(target0.scrollTop, 0, "table scroll y offset should be 0 in the end of the test");
+ assert_true(xScrollIsReceived && yScrollIsReceived, "target0 x and y scroll offsets should be greater than 0 after first two interactions (outside red border) respectively");
+ });
+ test_touchaction_cell.done();
+ updateDescriptionComplete();
+ });
+
+ on_event(btnComplete, 'pointerdown', function(event) {
+ detected_pointertypes[event.pointerType] = true;
+ });
+
+ on_event(target0, 'scroll', function(event) {
+ if(isFirstPart) {
+ xScr1 = target0.scrollLeft;
+ yScr1 = target0.scrollTop;
+
+ if(xScr1 != xScr0) {
+ xScrollIsReceived = true;
+ }
+
+ if(yScr1 != yScr0) {
+ test_touchaction_row.step(function () {
+ yScrollIsReceived = true;
+ });
+ updateDescriptionSecondStepTable(target0, scrollReturnInterval);
+ }
+
+ if(xScrollIsReceived && yScrollIsReceived) {
+ test_touchaction_row.done();
+ updateDescriptionThirdStepTable(target0, scrollReturnInterval, function() {
+ setTimeout(function() {
+ isFirstPart = false;
+ }, scrollReturnInterval); // avoid immediate triggering while scroll is still being performed
+ });
+ }
+ }
+ else {
+ test_touchaction_cell.step(failOnScroll, "scroll received while shouldn't");
+ }
+ });
+ }
+
+ function updateDescriptionSecondStepTable(target, returnInterval, element) {
+ window.setTimeout(function() {
+ objectScroller(target, 'up', 0);
+ }
+ , returnInterval);
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element RIGHT staring your touch over the Row 1";
+ }
+
+ function updateDescriptionThirdStepTable(target, returnInterval, callback = null) {
+ window.setTimeout(function() {
+ objectScroller(target, 'left', 0);
+ if (callback) {
+ callback();
+ }
+ }
+ , returnInterval);
+ document.getElementById('desc').innerHTML = "Test Description: Try to scroll element DOWN then RIGHT starting your touch inside of the Cell 3";
+ }
+
+ </script>
+ <h1>touch-action: none</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerevent_touch-action-verification.html b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-verification.html
new file mode 100644
index 0000000000..7800f2c9da
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerevent_touch-action-verification.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<html>
+ <head>
+ <title>touch-action: basic verification</title>
+ <meta name="assert" content="TA15.20 - The touch-action CSS property determines whether touch input MAY trigger default behavior supplied by the user agent.
+ auto: The user agent MAY determine any permitted touch behaviors, such as panning and zooming manipulations of the viewport, for touches that begin on the element.
+ none: Touches that begin on the element MUST NOT trigger default touch behaviors.
+ pan-x: The user agent MAY consider touches that begin on the element only for the purposes of horizontally scrolling the element's nearest ancestor with horizontally scrollable content.
+ pan-y: The user agent MAY consider touches that begin on the element only for the purposes of vertically scrolling the element's nearest ancestor with vertically scrollable content.
+ manipulation: The user agent MAY consider touches that begin on the element only for the purposes of scrolling and continuous zooming. Any additional behaviors supported by auto are out of scope for this specification.">
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="pointerevent_support.js"></script>
+ <style>
+ /*
+ Give some rules below something to override in order to test
+ that they really are being parsed
+ */
+ .defnone {
+ touch-action: none;
+ }
+ </style>
+ </head>
+ <body onload="run()">
+ <h2>Pointer Events touch-action attribute support</h2>
+ <h4 id="desc">Test Description: Test will automatically check parsing behaviour of various touch-action combinations.</h4>
+ <script type='text/javascript'>
+ var detected_pointertypes = {};
+
+ setup({ explicit_done: true });
+
+ function run() {
+ var tests = document.querySelectorAll('.test');
+ //TA 15.20
+ for (var i = 0; i < tests.length; i++) {
+ test(function() {
+ var style = window.getComputedStyle(tests[i]);
+ assert_equals(tests[i].attributes.expected.value, style.touchAction);
+ }, tests[i].id);
+ }
+ done();
+ }
+ </script>
+ <h1>touch-action: basic verification</h1>
+ <div id="complete-notice">
+ <p>The following pointer types were detected: <span id="pointertype-log"></span>.</p>
+ </div>
+ <div id="log"></div>
+ <div class="test" id="default" expected="auto"></div>
+ <div class="test defnone" id="stylesheet-none" expected="none"></div>
+ <div class="test defnone" id="explicit-auto" style="touch-action: auto;" expected="auto"></div>
+ <div class="test" id="explicit-pan-x" style="touch-action: pan-x;" expected="pan-x"></div>
+ <div class="test" id="explicit-pan-left" style="touch-action: pan-left;" expected="pan-left"></div>
+ <div class="test" id="explicit-pan-right" style="touch-action: pan-right;" expected="pan-right"></div>
+ <div class="test" id="explicit-pan-y" style="touch-action: pan-y;" expected="pan-y"></div>
+ <div class="test" id="explicit-pan-up" style="touch-action: pan-up;" expected="pan-up"></div>
+ <div class="test" id="explicit-pan-down" style="touch-action: pan-down;" expected="pan-down"></div>
+ <div class="test" id="explicit-pan-x-pan-y" style="touch-action: pan-x pan-y;" expected="pan-x pan-y"></div>
+ <div class="test" id="explicit-pan-y-pan-x" style="touch-action: pan-y pan-x;" expected="pan-x pan-y"></div>
+ <div class="test" id="explicit-pan-left-pan-up" style="touch-action: pan-left pan-up;" expected="pan-left pan-up"></div>
+ <div class="test" id="explicit-pan-left-pan-down" style="touch-action: pan-left pan-down;" expected="pan-left pan-down"></div>
+ <div class="test" id="explicit-pan-right-pan-up" style="touch-action: pan-right pan-up;" expected="pan-right pan-up"></div>
+ <div class="test" id="explicit-pan-right-pan-down" style="touch-action: pan-right pan-down;" expected="pan-right pan-down"></div>
+ <div class="test" id="explicit-pan-up-pan-left" style="touch-action: pan-up pan-left;" expected="pan-left pan-up"></div>
+ <div class="test" id="explicit-pan-up-pan-right" style="touch-action: pan-up pan-right;" expected="pan-right pan-up"></div>
+ <div class="test" id="explicit-pan-down-pan-left" style="touch-action: pan-down pan-left;" expected="pan-left pan-down"></div>
+ <div class="test" id="explicit-pan-down-pan-right" style="touch-action: pan-down pan-right;" expected="pan-right pan-down"></div>
+ <div class="test" id="explicit-manipulation" style="touch-action: manipulation;" expected="manipulation"></div>
+ <div class="test" id="explicit-none" style="touch-action: none;" expected="none"></div>
+ <div class="test" id="explicit-invalid-1" style="touch-action: bogus;" expected="auto"></div>
+ <div class="test defnone" id="explicit-invalid-2" style="touch-action: auto pan-x;" expected="none"></div>
+ <div class="test" id="explicit-invalid-3" style="touch-action: pan-y none;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-4" style="touch-action: pan-x pan-x;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-5" style="touch-action: manipulation pan-x;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-6" style="touch-action: pan-x pan-left;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-7" style="touch-action: auto pan-left;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-8" style="touch-action: none pan-left;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-9" style="touch-action: pan-x pan-right;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-10" style="touch-action: pan-y pan-up;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-11" style="touch-action: pan-y pan-down;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-12" style="touch-action: pan-left pan-right;" expected="auto"></div>
+ <div class="test" id="explicit-invalid-13" style="touch-action: pan-up pan-down;" expected="auto"></div>
+ <div style="touch-action: none;">
+ <div class="test" id="not-inherited" expected="auto"></div>
+ <div class="test" id="inherit" style="touch-action: inherit;" expected="none"></div>
+ </div>
+ <div class="test defnone" id="initial" style="touch-action: initial;" expected="auto"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/pointerlock/pointerevent_movementxy-manual.html b/dom/events/test/pointerevents/wpt/pointerlock/pointerevent_movementxy-manual.html
new file mode 100644
index 0000000000..5b0edd3c61
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerlock/pointerevent_movementxy-manual.html
@@ -0,0 +1,99 @@
+<!doctype html>
+<html>
+ <head>
+ <title>Pointer Events properties tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <!-- Additional helper script for common checks across event types -->
+ <script type="text/javascript" src="../pointerevent_support.js"></script>
+ <style>
+ #testContainer {
+ touch-action: none;
+ user-select: none;
+ position: relative;
+ }
+ #box1 {
+ top: 30px;
+ left: 50px;
+ background: black;
+ }
+ #box2 {
+ top: 70px;
+ left: 250px;
+ background: red;
+ }
+ #innerFrame {
+ top: 10px;
+ left: 100px;
+ }
+ #square2 {
+ visibility: block;
+ }
+ </style>
+ <script>
+ var expectedPointerId = NaN;
+ var startSummation = false;
+ var lastScreenX = 0;
+ var lastScreenY = 0;
+
+ function resetTestState() {
+ startSummation = false;
+ lastScreenX = 0;
+ lastScreenY = 0;
+ }
+
+ function run() {
+ var test_pointerEvent = setup_pointerevent_test("pointerevent attributes", ['mouse', 'touch']);
+
+ [document, document.getElementById('innerFrame').contentDocument].forEach(function(element) {
+ on_event(element, 'pointermove', function (event) {
+ if (startSummation) {
+ test_pointerEvent.step(function() {
+ assert_equals(event.movementX, event.screenX - lastScreenX, "movementX should be the delta between current event's and last event's screenX");
+ assert_equals(event.movementY, event.screenY - lastScreenY, "movementY should be the delta between current event's and last event's screenY");
+ });
+ lastScreenX = event.screenX;
+ lastScreenY = event.screenY;
+ }
+ });
+ });
+ on_event(document.querySelector('#box1'), 'pointerdown', function(event) {
+ event.target.releasePointerCapture(event.pointerId);
+ test_pointerEvent.step(function() {
+ assert_equals(event.pointerType, expectedPointerType, "Use the instructed pointer type.");
+ });
+ startSummation = true;
+ lastScreenX = event.screenX;
+ lastScreenY = event.screenY;
+ });
+ on_event(document.querySelector('#box2'), 'pointerup', function(event) {
+ startSummation = false;
+ test_pointerEvent.done();
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <h1>Pointer Events movementX/Y attribute test</h1>
+ <h2 id="pointerTypeDescription"></h2>
+ <h4>
+ Test Description: This test checks the properties of pointer events that do not support hover.
+ <ol>
+ <li>Press down on the black square.</li>
+ <li>Move your pointer slowly along a straight line to the red square.</li>
+ <li>Release the pointer when you are over the red square.</li>
+ </ol>
+
+ Test passes if the proper behavior of the events is observed.
+ </h4>
+ <div id="testContainer">
+ <div id="box1" class="square"></div>
+ <div id="box2" class="square"></div>
+ <iframe id="innerFrame" src="resources/pointerevent_movementxy-iframe.html"></iframe>
+ </div>
+ <div class="spacer"></div>
+ </body>
+</html>
+
diff --git a/dom/events/test/pointerevents/wpt/pointerlock/resources/pointerevent_movementxy-iframe.html b/dom/events/test/pointerevents/wpt/pointerlock/resources/pointerevent_movementxy-iframe.html
new file mode 100644
index 0000000000..627af3b61c
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/pointerlock/resources/pointerevent_movementxy-iframe.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width">
+ </head>
+ <body>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/resources/pointerevent_attributes_hoverable_pointers-iframe.html b/dom/events/test/pointerevents/wpt/resources/pointerevent_attributes_hoverable_pointers-iframe.html
new file mode 100644
index 0000000000..5e55868282
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/resources/pointerevent_attributes_hoverable_pointers-iframe.html
@@ -0,0 +1,10 @@
+<!doctype html>
+<html>
+ <head>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ </head>
+ <body>
+ <div id="square2" class="square"></div>
+ </body>
+</html>
diff --git a/dom/events/test/pointerevents/wpt/resources/pointerevent_pointerId_scope-iframe.html b/dom/events/test/pointerevents/wpt/resources/pointerevent_pointerId_scope-iframe.html
new file mode 100644
index 0000000000..ab33560b35
--- /dev/null
+++ b/dom/events/test/pointerevents/wpt/resources/pointerevent_pointerId_scope-iframe.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<html>
+ <!--
+Test cases for Pointer Events v1 spec
+This document references Test Assertions (abbrev TA below) written by Cathy Chan
+http://www.w3.org/wiki/PointerEvents/TestAssertions
+-->
+ <head>
+ <title>Pointer Events pointerdown tests</title>
+ <meta name="viewport" content="width=device-width">
+ <link rel="stylesheet" type="text/css" href="../pointerevent_styles.css">
+ <script>
+ function run() {
+ var target1 = document.getElementById("target1");
+
+ var eventList = ['pointerenter', 'pointerover', 'pointermove', 'pointerout', 'pointerleave'];
+
+ eventList.forEach(function(eventName) {
+ target1.addEventListener(eventName, function (event) {
+ var pass_data = {
+ 'pointerId' : event.pointerId,
+ 'type' : event.type,
+ 'pointerType' : event.pointerType
+ };
+ top.postMessage(JSON.stringify(pass_data), "*");
+ });
+ });
+ }
+ </script>
+ </head>
+ <body onload="run()">
+ <div id="target1" class="touchActionNone">
+ </div>
+ </body>
+</html>
diff --git a/dom/events/test/test_DataTransferItemList.html b/dom/events/test/test_DataTransferItemList.html
new file mode 100644
index 0000000000..1d093c8cf7
--- /dev/null
+++ b/dom/events/test/test_DataTransferItemList.html
@@ -0,0 +1,233 @@
+<html>
+<head>
+ <title>Tests for the DataTransferItemList object</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 style="height: 300px; overflow: auto;">
+<p id="display"> </p>
+<img id="image" draggable="true" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82">
+<div id="over" style="width: 100px; height: 100px; border: 2px black dashed;">
+ drag over here
+</div>
+
+<script>
+ function spin() {
+ // Defer to the event loop twice to wait for any events to be flushed out.
+ return new Promise(function(a) {
+ SimpleTest.executeSoon(function() {
+ SimpleTest.executeSoon(a)
+ });
+ });
+ }
+
+ add_task(async function() {
+ await spin();
+ var draggable = document.getElementById('image');
+ var over = document.getElementById('over');
+
+ var dragstartFired = 0;
+ draggable.addEventListener('dragstart', onDragStart);
+ function onDragStart(e) {
+ draggable.removeEventListener('dragstart', onDragStart);
+
+ var dt = e.dataTransfer;
+ dragstartFired++;
+
+ ok(true, "dragStart event fired");
+ var dtList = e.dataTransfer.items;
+ ok(dtList instanceof DataTransferItemList,
+ "DataTransfer.items returns a DataTransferItemList");
+
+ for (var i = 0; i < dtList.length; i++) {
+ var item = dtList[i];
+ ok(item instanceof DataTransferItem,
+ "operator[] returns DataTransferItem objects");
+ if (item.kind == "file") {
+ var file = item.getAsFile();
+ ok(file instanceof File, "getAsFile() returns File objects");
+ }
+ }
+
+ dtList.clear();
+ is(dtList.length, 0, "after .clear() DataTransferItemList should be empty");
+
+ dtList.add("this is some text", "text/plain");
+ dtList.add("<a href='www.mozilla.org'>this is a link</a>", "text/html");
+ dtList.add("http://www.mozilla.org", "text/uri-list");
+ dtList.add("this is custom-data", "custom-data");
+
+
+ var file = new File(['<a id="a"><b id="b">hey!</b></a>'], "myfile.html",
+ {type: "text/html"});
+
+ dtList.add(file);
+
+ checkTypes(["text/plain", "text/html", "text/uri-list", "custom-data", "text/html"],
+ dtList, "DataTransferItemList.add test");
+
+ var files = e.dataTransfer.files;
+ is(files.length, 1, "DataTransfer.files should contain the one file we added earlier");
+ is(files[0], file, "It should be the same file as the file we originally created");
+ is(file, e.dataTransfer.mozGetDataAt("text/html", 1),
+ "It should be stored in index 1 for mozGetDataAt");
+
+ var file2 = new File(['<a id="c"><b id="d">yo!</b></a>'], "myotherfile.html",
+ {type: "text/html"});
+ dtList.add(file2);
+
+ todo(files.length == 2, "This test has chrome privileges, so the FileList objects aren't updated live");
+ files = e.dataTransfer.files;
+ is(files.length, 2, "The files property should have been updated in place");
+ is(files[1], file2, "It should be the same file as the file we originally created");
+ is(file2, e.dataTransfer.mozGetDataAt("text/html", 2),
+ "It should be stored in index 2 for mozGetDataAt");
+
+ var oldLength = dtList.length;
+ var randomString = "foo!";
+ e.dataTransfer.mozSetDataAt("random/string", randomString, 3);
+ is(oldLength, dtList.length,
+ "Adding a non-file entry to a non-zero index should not add an item to the items list");
+
+ var file3 = new File(['<a id="e"><b id="f">heya!</b></a>'], "yetanotherfile.html",
+ {type: "text/html"});
+ e.dataTransfer.mozSetDataAt("random/string", file3, 3);
+ is(oldLength + 1, dtList.length,
+ "Replacing the entry with a file should add it to the list!");
+ is(dtList[oldLength].getAsFile(), file3, "It should be stored in the last index as a file");
+ is(dtList[oldLength].type, "text/html", "It should have the correct type");
+ is(dtList[oldLength].kind, "file", "It should have the correct kind");
+
+ todo(files.length == 3, "This test has chrome privileges, so the FileList objects aren't updated live");
+ files = e.dataTransfer.files;
+ is(files[files.length - 1], file3, "It should also be in the files list");
+
+ oldLength = dtList.length;
+ var nonstring = {};
+ e.dataTransfer.mozSetDataAt("jsobject", nonstring, 0);
+ is(oldLength + 1, dtList.length,
+ "Adding a non-string object using the mozAPIs to index 0 should add an item to the dataTransfer");
+ is(dtList[oldLength].type, "jsobject", "It should have the correct type");
+ is(dtList[oldLength].kind, "other", "It should have the correct kind");
+
+ // Clear the event's data and get it set up so we can read it later!
+ dtList.clear();
+
+ dtList.add(file);
+ dtList.add("this is some text", "text/plain");
+ is(e.dataTransfer.mozGetDataAt("text/html", 1), file);
+ }
+
+ var getAsStringCalled = 0;
+ var dragenterFired = 0;
+ over.addEventListener('dragenter', onDragEnter);
+ function onDragEnter(e) {
+ over.removeEventListener('dragenter', onDragEnter);
+
+ var dt = e.dataTransfer;
+ dragenterFired++;
+
+ // NOTE: This test is run with chrome privileges.
+ // For back-compat reasons, protected mode acts like readonly mode for
+ // chrome documents.
+ readOnly(e);
+ }
+
+ var dropFired = 0;
+ over.addEventListener('drop', onDrop);
+ function onDrop(e) {
+ over.removeEventListener('drop', onDrop);
+
+ var dt = e.dataTransfer;
+ dropFired++;
+ e.preventDefault();
+
+ readOnly(e);
+ }
+
+
+ function readOnly(e) {
+ var dtList = e.dataTransfer.items;
+ var num = dtList.length;
+
+ // .clear() should have no effect
+ dtList.clear();
+ is(dtList.length, num,
+ ".clear() should have no effect on the object during a readOnly event");
+
+ // .remove(i) should throw InvalidStateError
+ for (var i = 0; i < dtList.length; i++) {
+ expectError(function() { dtList.remove(i); },
+ "InvalidStateError", ".remove(" + i + ") during a readOnly event");
+ }
+
+ // .add() should return null and have no effect
+ var data = [["This is a plain string", "text/plain"],
+ ["This is <em>HTML!</em>", "text/html"],
+ ["http://www.mozilla.org/", "text/uri-list"],
+ ["this is some custom data", "custom-data"]];
+
+ for (var i = 0; i < data.length; i++) {
+ is(dtList.add(data[i][0], data[i][1]), null,
+ ".add() should return null during a readOnly event");
+
+ is(dtList.length, num, ".add() should have no effect during a readOnly event");
+ }
+
+ // .add() with a file should return null and have no effect
+ var file = new File(['<a id="a"><b id="b">hey!</b></a>'], "myfile.html",
+ {type: "text/html"});
+ is(dtList.add(file), null, ".add() with a file should return null during a readOnly event");
+ is(dtList.length, num, ".add() should have no effect during a readOnly event");
+
+ // We should be able to access the files
+ is(e.dataTransfer.files.length, 1, "Should be able to access files");
+ ok(e.dataTransfer.files[0], "File should be the same file!");
+ is(e.dataTransfer.items.length, 2, "Should be able to see there are 2 items");
+
+ is(e.dataTransfer.items[0].kind, "file", "First item should be a file");
+ is(e.dataTransfer.items[1].kind, "string", "Second item should be a string");
+
+ is(e.dataTransfer.items[0].type, "text/html", "first item should be text/html");
+ is(e.dataTransfer.items[1].type, "text/plain", "second item should be text/plain");
+
+ ok(e.dataTransfer.items[0].getAsFile(), "Should be able to get file");
+ e.dataTransfer.items[1].getAsString(function(s) {
+ getAsStringCalled++;
+ is(s, "this is some text", "Should provide the correct string");
+ });
+ }
+
+ synthesizeDrop(draggable, over, null, null);
+
+ // Wait for the getAsString callbacks to complete
+ await spin();
+ is(getAsStringCalled, 2, "getAsString should be called twice");
+
+ // Sanity-check to make sure that the events were actually run
+ is(dragstartFired, 1, "dragstart fired");
+ is(dragenterFired, 1, "dragenter fired");
+ is(dropFired, 1, "drop fired");
+ });
+
+ function expectError(fn, eid, testid) {
+ var error = "";
+ try {
+ fn();
+ } catch (ex) {
+ error = ex.name;
+ }
+ is(error, eid, testid + " causes exception " + eid);
+ }
+
+ function checkTypes(aExpectedList, aDtList, aTestid) {
+ is(aDtList.length, aExpectedList.length, aTestid + " length test");
+ for (var i = 0; i < aExpectedList.length; i++) {
+ is(aDtList[i].type, aExpectedList[i], aTestid + " type " + i);
+ }
+ }
+</script>
+
+</body>
+</html>
diff --git a/dom/events/test/test_accel_virtual_modifier.html b/dom/events/test/test_accel_virtual_modifier.html
new file mode 100644
index 0000000000..6d98053fa2
--- /dev/null
+++ b/dom/events/test/test_accel_virtual_modifier.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM "Accel" virtual modifier</title>
+ <script src="/tests/SimpleTest/SimpleTest.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>
+<pre id="test">
+<script type="application/javascript">
+
+var kAccel = "Accel";
+var kAccelKeyCode = SpecialPowers.getIntPref("ui.key.accelKey");
+
+var mouseEvent = new MouseEvent("mousedown", {});
+is(mouseEvent.getModifierState(kAccel), false,
+ "MouseEvent.getModifierState(\"" + kAccel + "\") should be false");
+mouseEvent = new MouseEvent("wheel", { accelKey: true});
+is(mouseEvent.getModifierState(kAccel), false,
+ "MouseEvent.getModifierState(\"" + kAccel + "\") should be false due to not supporting accelKey attribute");
+mouseEvent = new MouseEvent("mousedown", { ctrlKey: true });
+is(mouseEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_CONTROL,
+ "MouseEvent.getModifierState(\"" + kAccel + "\") should be true if ctrlKey is an accel modifier");
+mouseEvent = new MouseEvent("mousedown", { altKey: true });
+is(mouseEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_ALT,
+ "MouseEvent.getModifierState(\"" + kAccel + "\") should be true if altKey is an accel modifier");
+mouseEvent = new MouseEvent("mousedown", { metaKey: true });
+is(mouseEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_META,
+ "MouseEvent.getModifierState(\"" + kAccel + "\") should be true if metaKey is an accel modifier");
+mouseEvent = new MouseEvent("mousedown", { ctrlKey: true, altKey: true, metaKey: true });
+is(mouseEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_CONTROL ||
+ kAccelKeyCode == KeyboardEvent.DOM_VK_ALT ||
+ kAccelKeyCode == KeyboardEvent.DOM_VK_META,
+ "MouseEvent.getModifierState(\"" + kAccel + "\") should be true if one of ctrlKey, altKey or metaKey is an accel modifier");
+
+var wheelEvent = new WheelEvent("wheel", {});
+is(wheelEvent.getModifierState(kAccel), false,
+ "WheelEvent.getModifierState(\"" + kAccel + "\") should be false");
+wheelEvent = new WheelEvent("wheel", { accelKey: true});
+is(wheelEvent.getModifierState(kAccel), false,
+ "WheelEvent.getModifierState(\"" + kAccel + "\") should be false due to not supporting accelKey attribute");
+wheelEvent = new WheelEvent("wheel", { ctrlKey: true });
+is(wheelEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_CONTROL,
+ "WheelEvent.getModifierState(\"" + kAccel + "\") should be true if ctrlKey is an accel modifier");
+wheelEvent = new WheelEvent("wheel", { altKey: true });
+is(wheelEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_ALT,
+ "WheelEvent.getModifierState(\"" + kAccel + "\") should be true if altKey is an accel modifier");
+wheelEvent = new WheelEvent("wheel", { metaKey: true });
+is(wheelEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_META,
+ "WheelEvent.getModifierState(\"" + kAccel + "\") should be true if metaKey is an accel modifier");
+wheelEvent = new WheelEvent("wheel", { ctrlKey: true, altKey: true, metaKey: true });
+is(wheelEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_CONTROL ||
+ kAccelKeyCode == KeyboardEvent.DOM_VK_ALT ||
+ kAccelKeyCode == KeyboardEvent.DOM_VK_META,
+ "WheelEvent.getModifierState(\"" + kAccel + "\") should be true if one of ctrlKey, altKey or metaKey is an accel modifier");
+
+var keyboardEvent = new KeyboardEvent("keydown", {});
+is(keyboardEvent.getModifierState(kAccel), false,
+ "KeyboardEvent.getModifierState(\"" + kAccel + "\") should be false");
+keyboardEvent = new KeyboardEvent("keydown", { accelKey: true});
+is(keyboardEvent.getModifierState(kAccel), false,
+ "KeyboardEvent.getModifierState(\"" + kAccel + "\") should be false due to not supporting accelKey attribute");
+keyboardEvent = new KeyboardEvent("keydown", { ctrlKey: true });
+is(keyboardEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_CONTROL,
+ "KeyboardEvent.getModifierState(\"" + kAccel + "\") should be true if ctrlKey is an accel modifier");
+keyboardEvent = new KeyboardEvent("keydown", { altKey: true });
+is(keyboardEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_ALT,
+ "KeyboardEvent.getModifierState(\"" + kAccel + "\") should be true if altKey is an accel modifier");
+keyboardEvent = new KeyboardEvent("keydown", { metaKey: true });
+is(keyboardEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_META,
+ "KeyboardEvent.getModifierState(\"" + kAccel + "\") should be true if metaKey is an accel modifier");
+keyboardEvent = new KeyboardEvent("keydown", { ctrlKey: true, altKey: true, metaKey: true });
+is(keyboardEvent.getModifierState(kAccel), kAccelKeyCode == KeyboardEvent.DOM_VK_CONTROL ||
+ kAccelKeyCode == KeyboardEvent.DOM_VK_ALT ||
+ kAccelKeyCode == KeyboardEvent.DOM_VK_META,
+ "KeyboardEvent.getModifierState(\"" + kAccel + "\") should be true if one of ctrlKey, altKey or metaKey is an accel modifier");
+
+// "Accel" virtual modifier must be supported with getModifierState(). So, any legacy init*Event()'s
+// modifiers list argument shouldn't accept "Accel".
+ok(typeof(KeyboardEvent.initKeyboardEvent) != "function",
+ "If we would support KeyboardEvent.initKeyboardEvent, we should test its modifier list argument doesn't accept \"" + kAccel + "\"");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_accesskey.html b/dom/events/test/test_accesskey.html
new file mode 100644
index 0000000000..cdfff54a28
--- /dev/null
+++ b/dom/events/test/test_accesskey.html
@@ -0,0 +1,160 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Test for Accesskey</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>
+<p id="display"></p>
+<button id="activation" accesskey="e">Should be activated</button>
+<!-- Tests for label -->
+<label id="label1" accesskey="a">Label 1</label><br>
+<label id="label2" accesskey="a" for="checkbox2">Label 2</label><input type="checkbox" id="checkbox2" disabled>Checkbox 2</input><br>
+<label id="label3" accesskey="a" for="checkbox3">Label 3</label><input type="checkbox" id="checkbox3">Checkbox 3</input><br>
+<!-- Tests for button -->
+<button id="button1" accesskey="b" style="display: none;">Button 1</button><br>
+<button id="button2" accesskey="b">Button 2</button><br>
+<button id="button3" accesskey="b" disabled>Button 3</button><br>
+<button id="button4" accesskey="b">Button 4</button><br>
+<!-- Tests for legend -->
+<fieldset>
+<legend accesskey="c">Legend 1</legend>
+<input type="radio" id="radio1" style="display: none;"><label for="radio1">Radio 1</label><br>
+<input type="radio" id="radio2" disabled><label for="radio2">Radio 2</label><br>
+<input type="radio" id="radio3"><label for="radio3">Radio 3</label><br>
+</fieldset>
+<!-- Tests for legend2 -->
+<fieldset>
+<legend accesskey="d">Legend 2</legend>
+<input type="radio" id="radio4" disabled><label for="radio4">Radio 4</label><br>
+</fieldset>
+<input type="text" id="text1" accesskey="d"><br>
+<!-- Tests for bug 1723010 -->
+<button id="button5" style="display:none" accesskey="1">Button 5</button>
+<button id="button6" style="display:none" accesskey="2">Button 6</button>
+<textarea id="textarea1" accesskey="2"></textarea>
+<!-- Test for file input -->
+<input type=file id="file" accesskey="f">
+<script>
+
+function performAccessKey(aKey) {
+ synthesizeKey(aKey, (navigator.platform.includes("Mac")) ?
+ { altKey : true, ctrlKey : true } :
+ { altKey : true, shiftKey: true });
+}
+
+add_setup(async function() {
+ // A workaround for bug 1726811.
+ await SpecialPowers.pushPrefEnv({ set: [[ "accessibility.tabfocus", 7 ]] });
+});
+
+add_task(function activation() {
+ ok(!SpecialPowers.wrap(document).hasValidTransientUserGestureActivation, "Shouldn't be activated yet");
+ performAccessKey("e");
+ is(document.activeElement, document.getElementById("activation"), "Focus moved");
+ ok(SpecialPowers.wrap(document).hasValidTransientUserGestureActivation, "Accesskey triggers activation");
+});
+
+add_task(function label() {
+ let checkbox3 = document.getElementById("checkbox3");
+
+ performAccessKey("a");
+ is(document.activeElement.id, checkbox3.id, `focus should move to ${checkbox3.id}`);
+ ok(!checkbox3.checked, `${checkbox3.id} should be still unchecked`);
+});
+
+add_task(function button() {
+ let button2 = document.getElementById("button2");
+ let button4 = document.getElementById("button4");
+
+ [button2, button4].forEach(function(element) {
+ element.addEventListener("click", function() {
+ ok(false, `${element.id} should not be clicked`);
+ });
+ });
+
+ performAccessKey("b");
+ is(document.activeElement.id, button2.id, `focus should move to ${button2.id}`);
+
+ performAccessKey("b");
+ is(document.activeElement.id, button4.id, `focus should move to ${button4.id}`);
+});
+
+add_task(function legend() {
+ let radio3 = document.getElementById("radio3");
+
+ performAccessKey("c");
+ is(document.activeElement.id, radio3.id, `focus should move to ${radio3.id}`);
+ ok(!radio3.checked, `${radio3.id} should be still unchecked`);
+});
+
+add_task(function legend2() {
+ let text1 = document.getElementById("text1");
+
+ performAccessKey("d");
+ is(document.activeElement.id, text1.id, `focus should move to ${text1.id}`);
+});
+
+/** Test for Bug 1723010 **/
+
+add_task(async function removeElement() {
+ let button5 = document.getElementById("button5");
+ let textarea1 = document.getElementById("textarea1");
+ let promise = new Promise((resolve) => {
+ button5.addEventListener("click", function() {
+ textarea1.remove();
+ SimpleTest.executeSoon(() => {
+ ok(true, "should not crash");
+ resolve();
+ });
+ }, { once: true });
+ });
+
+ performAccessKey("1");
+ await promise;
+});
+
+add_task(async function modifyAccessKey() {
+ let button5 = document.getElementById("button5");
+ let button6 = document.getElementById("button6");
+ let textarea1 = document.querySelector("textarea1");
+ let promise = new Promise((resolve) => {
+ button5.addEventListener("click", function() {
+ button5.setAttribute("accesskey", "2");
+ button6.setAttribute("accesskey", "1");
+ SimpleTest.executeSoon(() => {
+ ok(true, "Button 5 should be clicked");
+ resolve();
+ });
+ }, { once: true });
+
+ button6.addEventListener("click", function() {
+ ok(false, "Button 6 should not be clicked");
+ }, { once: true });
+ });
+
+ performAccessKey("1");
+ await promise;
+});
+
+add_task(async function file_picker() {
+ const file = document.getElementById("file");
+ const MockFilePicker = SpecialPowers.MockFilePicker;
+ MockFilePicker.init(window);
+ MockFilePicker.returnValue = MockFilePicker.returnCancel;
+
+ let clicked = false;
+ file.addEventListener("click", function(e) { clicked = true; });
+
+ performAccessKey("f");
+ ok(clicked, "Should've activated the picker");
+
+ MockFilePicker.reset();
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_addEventListenerExtraArg.html b/dom/events/test/test_addEventListenerExtraArg.html
new file mode 100644
index 0000000000..58b18545de
--- /dev/null
+++ b/dom/events/test/test_addEventListenerExtraArg.html
@@ -0,0 +1,31 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=828554
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 828554</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 828554 **/
+ SimpleTest.waitForExplicitFinish();
+ window.addEventListener("message", function() {
+ ok(true, "We got called");
+ SimpleTest.finish();
+ }, false, undefined);
+ window.postMessage("Hey there", "*");
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=828554">Mozilla Bug 828554</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_all_synthetic_events.html b/dom/events/test/test_all_synthetic_events.html
new file mode 100644
index 0000000000..d8de1a9148
--- /dev/null
+++ b/dom/events/test/test_all_synthetic_events.html
@@ -0,0 +1,488 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test all synthetic 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>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * kEventConstructors is a helper and database of all events.
+ * The sort order of the definition is by A to Z (ignore the Event postfix).
+ *
+ * XXX: should we move this into EventUtils.js?
+ *
+ * create: function or null. If this is null, it's impossible to create untrusted event for it.
+ * Otherwise, create(aName, aProps) returns an instance of the event initialized with aProps.
+ * aName specifies the event's type name. See each create() code for the detail of aProps.
+ */
+const kEventConstructors = {
+ Event: { create (aName, aProps) {
+ return new Event(aName, aProps);
+ },
+ },
+ AnimationEvent: { create (aName, aProps) {
+ return new AnimationEvent(aName, aProps);
+ },
+ },
+ AnimationPlaybackEvent: { create (aName, aProps) {
+ return new AnimationPlaybackEvent(aName, aProps);
+ },
+ },
+ AudioProcessingEvent: { create: null, // Cannot create untrusted event from JS.
+ },
+ BeforeUnloadEvent: { create (aName, aProps) {
+ var e = document.createEvent("beforeunloadevent");
+ e.initEvent(aName, aProps.bubbles, aProps.cancelable);
+ return e;
+ },
+ },
+ BlobEvent: { create (aName, aProps) {
+ return new BlobEvent(aName, {
+ data: new Blob([]),
+ });
+ },
+ },
+ CallEvent: { create (aName, aProps) {
+ return new CallEvent(aName, aProps);
+ },
+ },
+ CallGroupErrorEvent: { create (aName, aProps) {
+ return new CallGroupErrorEvent(aName, aProps);
+ },
+ },
+ CFStateChangeEvent: { create (aName, aProps) {
+ return new CFStateChangeEvent(aName, aProps);
+ },
+ },
+ CloseEvent: { create (aName, aProps) {
+ return new CloseEvent(aName, aProps);
+ },
+ },
+ ClipboardEvent: { create (aName, aProps) {
+ return new ClipboardEvent(aName, aProps);
+ },
+ },
+ CompositionEvent: { create (aName, aProps) {
+ var e = document.createEvent("compositionevent");
+ e.initCompositionEvent(aName, aProps.bubbles, aProps.cancelable,
+ aProps.view, aProps.data, aProps.locale);
+ return e;
+ },
+ },
+ ContentVisibilityAutoStateChangeEvent: { create (aName, aProps) {
+ return new ContentVisibilityAutoStateChangeEvent(aName, aProps);
+ },
+ },
+ CustomEvent: { create (aName, aProps) {
+ return new CustomEvent(aName, aProps);
+ },
+ },
+ DataErrorEvent: { create (aName, aProps) {
+ return new DataErrorEvent(aName, aProps);
+ },
+ },
+ DeviceLightEvent: { create (aName, aProps) {
+ return new DeviceLightEvent(aName, aProps);
+ },
+ },
+ DeviceMotionEvent: { create (aName, aProps) {
+ var e = document.createEvent("devicemotionevent");
+ e.initDeviceMotionEvent(aName, aProps.bubbles, aProps.cancelable, aProps.acceleration,
+ aProps.accelerationIncludingGravity, aProps.rotationRate,
+ aProps.interval || 0.0);
+ return e;
+ },
+ },
+ DeviceOrientationEvent: { create (aName, aProps) {
+ return new DeviceOrientationEvent(aName, aProps);
+ },
+ },
+ DeviceProximityEvent: { create (aName, aProps) {
+ return new DeviceProximityEvent(aName, aProps);
+ },
+ },
+ DownloadEvent: { create (aName, aProps) {
+ return new DownloadEvent(aName, aProps);
+ },
+ },
+ DragEvent: { create (aName, aProps) {
+ var e = document.createEvent("dragevent");
+ e.initDragEvent(aName, aProps.bubbles, aProps.cancelable,
+ aProps.view, aProps.detail,
+ aProps.screenX, aProps.screenY,
+ aProps.clientX, aProps.clientY,
+ aProps.ctrlKey, aProps.altKey, aProps.shiftKey, aProps.metaKey,
+ aProps.button, aProps.relatedTarget, aProps.dataTransfer);
+ return e;
+ },
+ },
+ ErrorEvent: { create (aName, aProps) {
+ return new ErrorEvent(aName, aProps);
+ },
+ },
+ FocusEvent: { create (aName, aProps) {
+ return new FocusEvent(aName, aProps);
+ },
+ },
+ FontFaceSetLoadEvent: { create (aName, aProps) {
+ return new FontFaceSetLoadEvent(aName, aProps);
+ },
+ },
+ FormDataEvent: { create (aName, aProps) {
+ return new FormDataEvent(aName, {
+ formData: new FormData()
+ });
+ },
+ },
+ GamepadEvent: { create (aName, aProps) {
+ return new GamepadEvent(aName, aProps);
+ },
+ },
+ GamepadAxisMoveEvent: { create (aName, aProps) {
+ return new GamepadAxisMoveEvent(aName, aProps);
+ },
+ },
+ GamepadButtonEvent: { create (aName, aProps) {
+ return new GamepadButtonEvent(aName, aProps);
+ },
+ },
+ GPUUncapturedErrorEvent: { create: null, //TODO: constructor test
+ },
+ HashChangeEvent: { create (aName, aProps) {
+ return new HashChangeEvent(aName, aProps);
+ },
+ },
+ IDBVersionChangeEvent: { create (aName, aProps) {
+ return new IDBVersionChangeEvent(aName, aProps);
+ },
+ },
+ ImageCaptureErrorEvent: { create (aName, aProps) {
+ return new ImageCaptureErrorEvent(aName, aProps);
+ },
+ },
+ InputEvent: { create (aName, aProps) {
+ return new InputEvent(aName, aProps);
+ },
+ },
+ KeyEvent: { create (aName, aProps) {
+ return new KeyboardEvent(aName, aProps);
+ },
+ },
+ KeyboardEvent: { create (aName, aProps) {
+ return new KeyboardEvent(aName, aProps);
+ },
+ },
+ MediaEncryptedEvent: { create (aName, aProps) {
+ return new MediaEncryptedEvent(aName, aProps);
+ },
+ },
+ MediaKeyMessageEvent: { create (aName, aProps) {
+ return new MediaKeyMessageEvent(aName, {
+ messageType: "license-request",
+ message: new ArrayBuffer(0)
+ });
+ },
+ },
+ MediaQueryListEvent: { create (aName, aProps) {
+ return new MediaQueryListEvent(aName, aProps);
+ },
+ },
+ MediaRecorderErrorEvent: { create (aName, aProps) {
+ aProps.error = new DOMException();
+ return new MediaRecorderErrorEvent(aName, aProps);
+ },
+ },
+ MediaStreamEvent: { create (aName, aProps) {
+ return new MediaStreamEvent(aName, aProps);
+ },
+ },
+ MediaStreamTrackEvent: {
+ // Difficult to test required arguments.
+ },
+ MessageEvent: { create (aName, aProps) {
+ var e = new MessageEvent("messageevent", { bubbles: aProps.bubbles,
+ cancelable: aProps.cancelable, data: aProps.data, origin: aProps.origin,
+ lastEventId: aProps.lastEventId, source: aProps.source });
+ return e;
+ },
+ },
+ MouseEvent: { create (aName, aProps) {
+ return new MouseEvent(aName, aProps);
+ },
+ },
+ MouseScrollEvent: { create: null
+ // Cannot create untrusted event from JS
+ },
+ MozApplicationEvent: { create (aName, aProps) {
+ return new MozApplicationEvent(aName, aProps);
+ },
+ },
+ MozClirModeEvent: { create (aName, aProps) {
+ return new MozClirModeEvent(aName, aProps);
+ },
+ },
+ MozContactChangeEvent: { create (aName, aProps) {
+ return new MozContactChangeEvent(aName, aProps);
+ },
+ },
+ MozEmergencyCbModeEvent: { create (aName, aProps) {
+ return new MozEmergencyCbModeEvent(aName, aProps);
+ },
+ },
+ MozMessageDeletedEvent: { create (aName, aProps) {
+ return new MozMessageDeletedEvent(aName, aProps);
+ },
+ },
+ MozMmsEvent: { create (aName, aProps) {
+ return new MozMmsEvent(aName, aProps);
+ },
+ },
+ MozOtaStatusEvent: { create (aName, aProps) {
+ return new MozOtaStatusEvent(aName, aProps);
+ },
+ },
+ MozSmsEvent: { create (aName, aProps) {
+ return new MozSmsEvent(aName, aProps);
+ },
+ },
+ MozStkCommandEvent: { create (aName, aProps) {
+ return new MozStkCommandEvent(aName, aProps);
+ },
+ },
+ MutationEvent: { create (aName, aProps) {
+ var e = document.createEvent("mutationevent");
+ e.initMutationEvent(aName, aProps.bubbles, aProps.cancelable,
+ aProps.relatedNode, aProps.prevValue, aProps.newValue,
+ aProps.attrName, aProps.attrChange);
+ return e;
+ },
+ },
+ OfflineAudioCompletionEvent: { create: "AudioContext" in self
+ ? function (aName, aProps) {
+ var ac = new AudioContext();
+ var ab = new AudioBuffer({ length: 42, sampleRate: ac.sampleRate });
+ aProps.renderedBuffer = ab;
+ return new OfflineAudioCompletionEvent(aName, aProps);
+ }
+ : null,
+ },
+ PageTransitionEvent: { create (aName, aProps) {
+ return new PageTransitionEvent(aName, aProps);
+ },
+ },
+ PointerEvent: { create (aName, aProps) {
+ return new PointerEvent(aName, aProps);
+ },
+ },
+ PopStateEvent: { create (aName, aProps) {
+ return new PopStateEvent(aName, aProps);
+ },
+ },
+ PopupBlockedEvent: { create (aName, aProps) {
+ return new PopupBlockedEvent(aName, aProps);
+ },
+ },
+ ProgressEvent: { create (aName, aProps) {
+ return new ProgressEvent(aName, aProps);
+ },
+ },
+ PromiseRejectionEvent: { create (aName, aProps) {
+ aProps.promise = new Promise(() => {});
+ return new PromiseRejectionEvent(aName, aProps);
+ },
+ },
+ RTCDataChannelEvent: { create (aName, aProps) {
+ let pc = new RTCPeerConnection();
+ aProps.channel = pc.createDataChannel("foo");
+ let e = new RTCDataChannelEvent(aName, aProps);
+ aProps.channel.close();
+ pc.close();
+ return e;
+ },
+ },
+ RTCDTMFToneChangeEvent: { create (aName, aProps) {
+ return new RTCDTMFToneChangeEvent(aName, aProps);
+ },
+ },
+ RTCPeerConnectionIceEvent: { create (aName, aProps) {
+ return new RTCPeerConnectionIceEvent(aName, aProps);
+ },
+ },
+ RTCTrackEvent: {
+ // Difficult to test required arguments.
+ },
+ ScrollAreaEvent: { create: null
+ // Cannot create untrusted event from JS
+ },
+ SpeechRecognitionError: { create (aName, aProps) {
+ return new SpeechRecognitionError(aName, aProps);
+ },
+ },
+ SpeechRecognitionEvent: { create (aName, aProps) {
+ return new SpeechRecognitionEvent(aName, aProps);
+ },
+ },
+ SpeechSynthesisErrorEvent: { create (aName, aProps) {
+ aProps.error = "synthesis-unavailable";
+ aProps.utterance = new SpeechSynthesisUtterance("Hello World");
+ return new SpeechSynthesisErrorEvent(aName, aProps);
+ },
+ },
+ SpeechSynthesisEvent: { create (aName, aProps) {
+ aProps.utterance = new SpeechSynthesisUtterance("Hello World");
+ return new SpeechSynthesisEvent(aName, aProps);
+ },
+ },
+ StorageEvent: { create (aName, aProps) {
+ return new StorageEvent(aName, aProps);
+ },
+ },
+ StyleSheetApplicableStateChangeEvent: { create (aName, aProps) {
+ return new StyleSheetApplicableStateChangeEvent(aName, aProps);
+ },
+ chromeOnly: true,
+ },
+ StyleSheetRemovedEvent: { create (aName, aProps) {
+ return new StyleSheetRemovedEvent(aName, aProps);
+ },
+ chromeOnly: true,
+ },
+ SubmitEvent: { create (aName, aProps) {
+ return new SubmitEvent(aName, aProps);
+ },
+ },
+ TaskPriorityChangeEvent: { create (aName, aProps) {
+ aProps.previousPriority = "user-blocking";
+ return new TaskPriorityChangeEvent(aName, aProps);
+ },
+ },
+ TCPSocketErrorEvent: { create(aName, aProps) {
+ return new TCPSocketErrorEvent(aName, aProps);
+ },
+ },
+ TCPSocketEvent: { create(aName, aProps) {
+ return new TCPSocketEvent(aName, aProps);
+ },
+ },
+ TCPServerSocketEvent: { create(aName, aProps) {
+ return new TCPServerSocketEvent(aName, aProps);
+ },
+ },
+ TimeEvent: { create: null
+ // Cannot create untrusted event from JS
+ },
+ ToggleEvent: { create(aName, aProps) {
+ return new ToggleEvent(aName, aProps);
+ },
+ },
+ TouchEvent: { create (aName, aProps) {
+ var e = document.createEvent("touchevent");
+ e.initTouchEvent(aName, aProps.bubbles, aProps.cancelable,
+ aProps.view, aProps.detail,
+ aProps.ctrlKey, aProps.altKey, aProps.shiftKey, aProps.metaKey,
+ aProps.touches, aProps.targetTouches, aProps.changedTouches);
+ return e;
+ },
+ },
+ TrackEvent: { create (aName, aProps) {
+ return new TrackEvent(aName, aProps);
+ },
+ },
+ TransitionEvent: { create (aName, aProps) {
+ return new TransitionEvent(aName, aProps);
+ },
+ },
+ UIEvent: { create (aName, aProps) {
+ return new UIEvent(aName, aProps);
+ },
+ },
+ UserProximityEvent: { create (aName, aProps) {
+ return new UserProximityEvent(aName, aProps);
+ },
+ },
+ USSDReceivedEvent: { create (aName, aProps) {
+ return new USSDReceivedEvent(aName, aProps);
+ },
+ },
+ VRDisplayEvent: { create: null,
+ // Required argument expects a VRDisplay that can not
+ // be created from Javascript without physical VR hardware
+ // connected. When Bug 1229480 lands, this test can be
+ // updated to use the puppet VR device.
+ },
+ WheelEvent: { create (aName, aProps) {
+ return new WheelEvent(aName, aProps);
+ },
+ },
+ WebGLContextEvent: { create (aName, aProps) {
+ return new WebGLContextEvent(aName, aProps);
+ },
+ },
+ SecurityPolicyViolationEvent: { create (aName, aProps) {
+ return new SecurityPolicyViolationEvent(aName, aProps);
+ },
+ },
+};
+
+function test() {
+ for (var name of Object.keys(kEventConstructors)) {
+ if (!kEventConstructors[name].chromeOnly) {
+ continue;
+ }
+ if (window[name]) {
+ ok(false, name + " should be chrome only.");
+ }
+ window[name] = SpecialPowers.unwrap(SpecialPowers.wrap(window)[name]);
+ }
+
+ var props = Object.getOwnPropertyNames(window);
+ for (var i = 0; i < props.length; i++) {
+ // Assume that event object must be named as "FooBarEvent".
+ if (!props[i].match(/^([A-Z][a-zA-Z]+)?Event$/)) {
+ continue;
+ }
+ if (!kEventConstructors[props[i]]) {
+ ok(false, "Unknown event found: " + props[i]);
+ continue;
+ }
+ if (!kEventConstructors[props[i]].create) {
+ todo(false, "Cannot create untrusted event of " + props[i]);
+ continue;
+ }
+ ok(true, "Creating " + props[i] + "...");
+ var event = kEventConstructors[props[i]].create("foo", {});
+ if (!event) {
+ ok(false, "Failed to create untrusted event: " + props[i]);
+ continue;
+ }
+ if (typeof(event.getModifierState) == "function") {
+ const kModifiers = [ "Shift", "Control", "Alt", "AltGr", "Meta", "CapsLock", "ScrollLock", "NumLock", "Fn", "FnLock", "Symbol", "SymbolLock" ];
+ for (var j = 0; j < kModifiers.length; j++) {
+ ok(true, "Calling " + props[i] + ".getModifierState(" + kModifiers[j] + ")...");
+ var modifierState = event.getModifierState(kModifiers[j]);
+ ok(true, props[i] + ".getModifierState(" + kModifiers[j] + ") = " + modifierState);
+ }
+ }
+ }
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv(
+ {"set": [["dom.w3c_touch_events.legacy_apis.enabled", true],
+ ["layout.css.content-visibility.enabled", true]]},
+ function() {
+ test();
+ SimpleTest.finish();
+ });
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_auxclick_autoscroll_off.html b/dom/events/test/test_auxclick_autoscroll_off.html
new file mode 100644
index 0000000000..5dd7b95920
--- /dev/null
+++ b/dom/events/test/test_auxclick_autoscroll_off.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test auxclick works when general.autoScroll is off</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<body style="height: 110vh;">
+ <button id="target">button</button>
+</body>
+<script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ const target = document.getElementById("target");
+ target.addEventListener('auxclick', (e) => {
+ ok(true, "auxclick should work");
+ SimpleTest.finish();
+ });
+
+ synthesizeMouseAtCenter(target, { type: "mousedown", button: 1 });
+ synthesizeMouseAtCenter(target, { type: "mouseup", button: 1 });
+ }
+
+ SpecialPowers.pushPrefEnv({"set": [
+ ["general.autoScroll", false],
+ ]}, function() {
+ SimpleTest.waitForFocus(function() {
+ test();
+ });
+ });
+</script>
diff --git a/dom/events/test/test_bug1003432.html b/dom/events/test/test_bug1003432.html
new file mode 100644
index 0000000000..d74aefb886
--- /dev/null
+++ b/dom/events/test/test_bug1003432.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1003432
+-->
+<head>
+ <title>Test for Bug 1003432</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=1003432">Mozilla Bug 1003432</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1003432 **/
+// Test CustomEvent on worker
+SimpleTest.waitForExplicitFinish();
+var worker = new Worker("test_bug1003432.js");
+ok(worker, "Should have worker!");
+
+var count = 0;
+worker.onmessage = function(evt) {
+ is(evt.data.type, "foobar", "Should get 'foobar' event!");
+ is(evt.data.detail, "test", "Detail should be 'test'.");
+ ok(evt.data.bubbles, "Event should bubble!");
+ ok(evt.data.cancelable, "Event should be cancelable.");
+
+ // wait for test results of constructor and initCustomEvent
+ if (++count == 2) {
+ worker.terminate();
+ SimpleTest.finish();
+ }
+};
+
+worker.postMessage("");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1003432.js b/dom/events/test/test_bug1003432.js
new file mode 100644
index 0000000000..2f92ebf8da
--- /dev/null
+++ b/dom/events/test/test_bug1003432.js
@@ -0,0 +1,31 @@
+addEventListener(
+ "foobar",
+ function (evt) {
+ postMessage({
+ type: evt.type,
+ bubbles: evt.bubbles,
+ cancelable: evt.cancelable,
+ detail: evt.detail,
+ });
+ },
+ true
+);
+
+addEventListener(
+ "message",
+ function (evt) {
+ // Test the constructor of CustomEvent
+ var e = new CustomEvent("foobar", {
+ bubbles: true,
+ cancelable: true,
+ detail: "test",
+ });
+ dispatchEvent(e);
+
+ // Test initCustomEvent
+ e = new CustomEvent("foobar");
+ e.initCustomEvent("foobar", true, true, "test");
+ dispatchEvent(e);
+ },
+ true
+);
diff --git a/dom/events/test/test_bug1013412.html b/dom/events/test/test_bug1013412.html
new file mode 100644
index 0000000000..03730b09b4
--- /dev/null
+++ b/dom/events/test/test_bug1013412.html
@@ -0,0 +1,116 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1013412
+https://bugzilla.mozilla.org/show_bug.cgi?id=1168182
+-->
+<head>
+ <title>Test for Bug 1013412 and 1168182</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ #content {
+ height: 800px;
+ overflow: scroll;
+ }
+
+ #scroller {
+ height: 2000px;
+ background: repeating-linear-gradient(#EEE, #EEE 100px, #DDD 100px, #DDD 200px);
+ }
+
+ #scrollbox {
+ margin-top: 200px;
+ width: 500px;
+ height: 500px;
+ border-radius: 250px;
+ box-shadow: inset 0 0 0 60px #555;
+ background: #777;
+ }
+
+ #circle {
+ position: relative;
+ left: 240px;
+ top: 20px;
+ border: 10px solid white;
+ border-radius: 10px;
+ width: 0px;
+ height: 0px;
+ transform-origin: 10px 230px;
+ will-change: transform;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1013412">Mozilla Bug 1013412</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1168182">Mozilla Bug 1168182</a>
+<p id="display"></p>
+<div id="content">
+ <p>Scrolling the page should be async and scrolling over the dark circle should scroll the page and avoid rotating the white ball.</p>
+ <div id="scroller">
+ <div id="scrollbox">
+ <div id="circle"></div>
+ </div>
+ </div>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var rotation = 0;
+var rotationAdjusted = false;
+
+var incrementForMode = function (mode) {
+ switch (mode) {
+ case WheelEvent.DOM_DELTA_PIXEL: return 1;
+ case WheelEvent.DOM_DELTA_LINE: return 15;
+ case WheelEvent.DOM_DELTA_PAGE: return 400;
+ }
+ return 0;
+};
+
+document.getElementById("scrollbox").addEventListener("wheel", function (e) {
+ rotation += e.deltaY * incrementForMode(e.deltaMode) * 0.2;
+ document.getElementById("circle").style.transform = "rotate(" + rotation + "deg)";
+ rotationAdjusted = true;
+ e.preventDefault();
+});
+
+var iteration = 0;
+function runTest() {
+ var content = document.getElementById('content');
+ // enough iterations that we would scroll to the bottom of 'content'
+ if (iteration < 600 && content.scrollTop != content.scrollTopMax) {
+ iteration++;
+ sendWheelAndPaint(content, 100, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 },
+ runTest);
+ return;
+ }
+ var scrollbox = document.getElementById('scrollbox');
+ is(content.scrollTop, content.scrollTopMax, "We should have scrolled to the bottom of the scrollframe");
+ is(rotationAdjusted, false, "The rotation should not have been adjusted");
+ SimpleTest.finish();
+}
+
+function startTest() {
+ // If we allow smooth scrolling the "smooth" scrolling may cause the page to
+ // glide past the scrollbox (which is supposed to stop the scrolling) and so
+ // we might end up at the bottom of the page.
+ SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
+ ["test.events.async.enabled", true],
+ ["mousewheel.transaction.timeout", 100000],
+ ["dom.event.wheel-event-groups.enabled", true]]},
+ runTest);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(startTest, window);
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug1017086_enable.html b/dom/events/test/test_bug1017086_enable.html
new file mode 100644
index 0000000000..e7e6097c63
--- /dev/null
+++ b/dom/events/test/test_bug1017086_enable.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1017086
+-->
+ <head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1017086</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">
+ /** Test for Bug 1017086 **/
+ function prepareTest() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.executeSoon(startTest);
+ }
+ function startTest() {
+ var iframe = document.getElementById("testFrame");
+ iframe.src = "bug1017086_inner.html";
+ }
+ function part_of_checks(pointer_events, check, window, document, testelem) {
+ for(item in pointer_events) { check(true, pointer_events[item], window, "window"); }
+ /** TODO
+ for(item in pointer_events) { check(false, pointer_events[item], document, "document"); }
+ **/
+ for(item in pointer_events) { check(true, pointer_events[item], testelem, "element"); }
+ SimpleTest.finish();
+ }
+ </script>
+ </head>
+ <body onload="prepareTest()">
+ <iframe id="testFrame" height="700" width="700"></iframe>
+ </body>
+</html>
diff --git a/dom/events/test/test_bug1037990.html b/dom/events/test/test_bug1037990.html
new file mode 100644
index 0000000000..c148debcf7
--- /dev/null
+++ b/dom/events/test/test_bug1037990.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1037990
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1037990</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=1037990">Mozilla Bug 1037990</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script type="application/javascript">
+
+ /** Test for Bug 1037990 **/
+
+ var pre, node, detachedAccess, attachedAcess;
+
+ node = document.createElement('a');
+ node.href = 'http://example.org';
+ node.accessKey = 'e';
+ detachedAccess = node.accessKeyLabel;
+ info('[window.document] detached: ' + detachedAccess);
+ document.body.appendChild(node);
+ attachedAcess = node.accessKeyLabel;
+ info('[window.document] attached: ' + attachedAcess);
+ is(detachedAccess, attachedAcess, "Both values are same for the window.document");
+
+ var parser=new DOMParser();
+ var xmlDoc=parser.parseFromString("<root></root>","text/xml");
+ var nn = xmlDoc.createElementNS('http://www.w3.org/1999/xhtml','a');
+ nn.setAttribute('accesskey','t')
+ detachedAccess = nn.accessKeyLabel;
+ info('[xmlDoc] detached: ' + detachedAccess);
+ var root = xmlDoc.getElementsByTagName('root')[0];
+ root.appendChild(nn);
+ attachedAcess = nn.accessKeyLabel;
+ info('[xmlDoc] attached: ' + attachedAcess);
+ is(detachedAccess, attachedAcess, "Both values are same for the xmlDoc");
+
+ var myDoc = new Document();
+ var newnode = myDoc.createElementNS('http://www.w3.org/1999/xhtml','a');
+ newnode.href = 'http://example.org';
+ newnode.accessKey = 'f';
+ detachedAccess = newnode.accessKeyLabel;
+ info('[new document] detached: ' + detachedAccess);
+ myDoc.appendChild(newnode);
+ attachedAcess = newnode.accessKeyLabel;
+ info('[new document] attached: ' + attachedAcess);
+ is(detachedAccess, attachedAcess, "Both values are same for the new Document()");
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1079236.html b/dom/events/test/test_bug1079236.html
new file mode 100644
index 0000000000..4c89101574
--- /dev/null
+++ b/dom/events/test/test_bug1079236.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1079236
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1079236</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 1079236 **/
+
+ function runTests() {
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ iframe.contentDocument.body.innerHTML = '<div id="content"></div>';
+
+ var c = iframe.contentDocument.getElementById("content");
+ var sr = c.attachShadow({mode: 'open'});
+ sr.innerHTML = "<input type='file'" + ">";
+ var file = sr.firstChild;
+ is(file.type, "file");
+ file.offsetLeft; // Flush layout because dispatching mouse events.
+ iframe.contentDocument.body.onmousemove = function(e) {
+ is(e.target, c, "Event target should be the element in non-Shadow DOM");
+ if (e.originalTarget == file) {
+ is(e.originalTarget, file,
+ "type='file' implementation doesn't seem to have native anonymous content");
+ } else {
+ var wrapped = SpecialPowers.wrap(e.originalTarget);
+ isnot(wrapped, file, "Shouldn't have the same event.target and event.originalTarget");
+ }
+
+ ok(!("composedTarget" in e), "Events shouldn't have composedTarget in non-chrome context!");
+ e = SpecialPowers.wrap(e);
+ var composedTarget = SpecialPowers.unwrap(e.composedTarget);
+ is(composedTarget, file, "composedTarget should be the file object.");
+
+ SimpleTest.finish();
+ }
+
+ var r = file.getBoundingClientRect();
+ synthesizeMouse(file, r.width / 6, r.height / 2, { type: "mousemove"}, iframe.contentWindow);
+ iframe.contentDocument.body.onmousemove = null;
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ window.onload = () => {
+ SimpleTest.waitForFocus(runTests);
+ };
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1079236">Mozilla Bug 1079236</a>
+<p id="display"></p>
+<div id="content">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1127588.html b/dom/events/test/test_bug1127588.html
new file mode 100644
index 0000000000..ca80328790
--- /dev/null
+++ b/dom/events/test/test_bug1127588.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1127588
+-->
+<head>
+ <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=1127588">Mozilla Bug 1127588</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<script type="application/javascript">
+
+/** Test for Bug 1127588 **/
+
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function () {
+ let insertedEventCount = 0;
+ let insertedListener = function() {
+ insertedEventCount++;
+ };
+
+ let removedEventCount = 0;
+ let removedListener = function() {
+ removedEventCount++;
+ };
+
+ // Tests for no title element.
+ document.addEventListener('DOMNodeRemoved', removedListener);
+ document.addEventListener('DOMNodeInserted', insertedListener);
+ document.title = "Test for Bug 1127588";
+ document.removeEventListener('DOMNodeInserted', insertedListener);
+ document.removeEventListener('DOMNodeRemoved', removedListener);
+
+ // Check result.
+ is(insertedEventCount, 2, "Should get 'DOMNodeInserted' mutation event");
+ is(removedEventCount, 0, "Should not get 'DOMNodeRemoved' mutation event");
+
+ // Test for updating title element.
+ insertedEventCount = 0;
+ removedEventCount = 0;
+ document.addEventListener('DOMNodeRemoved', removedListener);
+ document.addEventListener('DOMNodeInserted', insertedListener);
+ // eslint-disable-next-line no-self-assign
+ document.title = document.title;
+ document.removeEventListener('DOMNodeInserted', insertedListener);
+ document.removeEventListener('DOMNodeRemoved', removedListener);
+
+ // Check result.
+ is(insertedEventCount, 1, "Should get 'DOMNodeInserted' mutation event");
+ is(removedEventCount, 1, "Should get 'DOMNodeRemoved' mutation event");
+
+ SimpleTest.finish();
+};
+
+</script>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug1128787-1.html b/dom/events/test/test_bug1128787-1.html
new file mode 100644
index 0000000000..f7f05523ab
--- /dev/null
+++ b/dom/events/test/test_bug1128787-1.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1128787
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1128787</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1128787 **/
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = function (aEvent) {
+ var blurEventFired = false;
+ var input = document.getElementsByTagName("input")[0];
+ input.addEventListener("blur", function (event) {
+ ok(true, "input element gets blur event correctly");
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ is(utils.IMEStatus, utils.IME_STATUS_ENABLED, "IME should be enabled");
+
+ SimpleTest.executeSoon(function () {
+ document.designMode = "off";
+
+ is(utils.IMEStatus, utils.IME_STATUS_DISABLED, "IME should be disabled");
+
+ SimpleTest.finish();
+ });
+ }, {once: true});
+ document.designMode = "on";
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1128787">Mozilla Bug 1128787</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<input type="button"/>
+<script>
+var input = document.getElementsByTagName("input")[0];
+input.focus();
+</script>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1128787-2.html b/dom/events/test/test_bug1128787-2.html
new file mode 100644
index 0000000000..cb10ea431c
--- /dev/null
+++ b/dom/events/test/test_bug1128787-2.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1128787
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1128787</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1128787 **/
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = function (aEvent) {
+ var blurEventFired = false;
+ var input = document.getElementsByTagName("input")[0];
+ input.addEventListener("blur", function (event) {
+ ok(true, "input element gets blur event correctly");
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ is(utils.IMEStatus, utils.IME_STATUS_ENABLED, "IME should be enabled");
+
+ SimpleTest.executeSoon(function () {
+ document.designMode = "off";
+
+ // XXX Should be fixed.
+ todo_is(utils.IMEStatus, utils.IME_STATUS_DISABLED, "IME should be disabled");
+
+ SimpleTest.finish();
+ });
+ }, {once: true});
+ document.designMode = "on";
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1128787">Mozilla Bug 1128787</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<p contenteditable="true"></p>
+<input type="button"/>
+<script>
+var input = document.getElementsByTagName("input")[0];
+input.focus();
+</script>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1128787-3.html b/dom/events/test/test_bug1128787-3.html
new file mode 100644
index 0000000000..d70b67bfbe
--- /dev/null
+++ b/dom/events/test/test_bug1128787-3.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1128787
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1128787</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="chrome://global/skin"/>
+ <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+
+ /** Test for Bug 1128787 **/
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = function (aEvent) {
+ var blurEventFired = false;
+ var input = document.getElementsByTagName("input")[0];
+ input.addEventListener("blur", function (event) {
+ ok(true, "input element gets blur event correctly");
+
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ is(utils.IMEStatus, utils.IME_STATUS_ENABLED, "IME should be enabled");
+
+ is(document.designMode, "on",
+ "designMode should be \"on\" when blur event caused by enabling designMode is fired");
+ document.designMode = "off";
+ is(document.designMode, "off",
+ "designMode should become \"off\" even if it's reset by the blur event handler caused by enabling designMode");
+
+ is(utils.IMEStatus, utils.IME_STATUS_DISABLED, "IME should be disabled");
+ SimpleTest.finish();
+ }, {once: true});
+ document.designMode = "on";
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1128787">Mozilla Bug 1128787</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<input type="button"/>
+<script>
+var input = document.getElementsByTagName("input")[0];
+input.focus();
+</script>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1145910.html b/dom/events/test/test_bug1145910.html
new file mode 100644
index 0000000000..2f7942937c
--- /dev/null
+++ b/dom/events/test/test_bug1145910.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1145910
+-->
+<head>
+ <title>Test for Bug 1145910</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 type="application/javascript">
+
+/** Test for Bug 1145910 **/
+
+function runTests() {
+
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ iframe.contentDocument.body.innerHTML =
+ '<style> div:active { color: rgb(0, 255, 0); } </style> <div id="host">Foo</div>';
+
+ var host = iframe.contentDocument.getElementById("host");
+ var shadow = host.attachShadow({mode: 'open'});
+ shadow.innerHTML = '<style>div:active { color: rgb(0, 255, 0); }</style><div id="inner">Bar</div>';
+ var inner = shadow.getElementById("inner");
+ var iframeWin = iframe.contentWindow;
+
+ is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "The host should not be active");
+ is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+
+ synthesizeMouseAtCenter(host, { type: "mousedown" }, iframeWin);
+
+ is(iframeWin.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+ is(iframeWin.getComputedStyle(host).color, "rgb(0, 255, 0)", "Host should be active when the inner div is made active.");
+
+ synthesizeMouseAtCenter(host, { type: "mouseup" }, iframeWin);
+
+ is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+ is(iframeWin.getComputedStyle(host).color, "rgb(0, 0, 0)", "Host should no longer be active.");
+
+ SimpleTest.finish();
+};
+
+SimpleTest.waitForExplicitFinish();
+window.onload = () => {
+ SimpleTest.waitForFocus(runTests);
+};
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1150308.html b/dom/events/test/test_bug1150308.html
new file mode 100644
index 0000000000..e9f9b480de
--- /dev/null
+++ b/dom/events/test/test_bug1150308.html
@@ -0,0 +1,49 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1150308
+-->
+<head>
+ <title>Test for Bug 1150308</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 type="application/javascript">
+
+/** Test for Bug 1150308 **/
+
+function runTests() {
+ var iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ iframe.contentDocument.body.innerHTML =
+ '<div id="host"><span id="distributeme">Foo</span></div>';
+
+ var host = iframe.contentDocument.getElementById("host");
+ var shadow = host.attachShadow({mode: 'open'});
+ shadow.innerHTML = '<style>.bar:active { color: rgb(0, 255, 0); }</style><div class="bar" id="inner"><slot></slot></div>';
+ var inner = shadow.getElementById("inner");
+ var distributed = iframe.contentDocument.getElementById("distributeme");
+ var iframeWin = iframe.contentWindow;
+
+ is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "The div inside the shadow root should not be active.");
+
+ synthesizeMouseAtCenter(distributed, { type: "mousedown" }, iframeWin);
+
+ is(iframeWin.getComputedStyle(inner).color, "rgb(0, 255, 0)", "Div inside shadow root should be active.");
+
+ synthesizeMouseAtCenter(distributed, { type: "mouseup" }, iframeWin);
+
+ is(iframeWin.getComputedStyle(inner).color, "rgb(0, 0, 0)", "Div inside shadow root should no longer be active.");
+
+ SimpleTest.finish();
+};
+
+SimpleTest.waitForExplicitFinish();
+window.onload = () => {
+ SimpleTest.waitForFocus(runTests);
+};
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1248459.html b/dom/events/test/test_bug1248459.html
new file mode 100644
index 0000000000..40aa162f7e
--- /dev/null
+++ b/dom/events/test/test_bug1248459.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1248459
+-->
+<head>
+ <title>Test for Bug 1248459</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>
+<input id="input" value="foo">
+<div id="div">bar</div>
+<script type="application/javascript">
+
+/** Test for Bug 1248459 **/
+/**
+ * The bug occurs when a piece of text outside of the editor's root element is
+ * somehow selected when the editor is focused. In the bug's case, it's the
+ * placeholder anonymous div that's selected. In this test's case, it's a
+ * document div that's selected.
+ */
+SimpleTest.waitForExplicitFinish();
+
+SimpleTest.waitForFocus(function() {
+ var div = document.getElementById("div");
+ var input = document.getElementById("input");
+
+ input.appendChild(div);
+ input.focus();
+
+ var editor = SpecialPowers.wrap(input).editor;
+ var sel = editor.selection;
+
+ sel.selectAllChildren(editor.rootElement);
+ var result = synthesizeQuerySelectedText();
+
+ ok(result.succeeded, "Query selected text should succeed");
+ is(result.offset, 0, "Selected text should be at offset 0");
+ is(result.text, "foo", "Selected text should match");
+
+ var range = document.createRange();
+ range.selectNode(div);
+
+ sel.removeAllRanges();
+ sel.addRange(range);
+
+ result = synthesizeQuerySelectedText();
+
+ ok(!result.succeeded, "Query out-of-bounds selection should fail");
+
+ SimpleTest.finish();
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1264380.html b/dom/events/test/test_bug1264380.html
new file mode 100644
index 0000000000..9349e9712f
--- /dev/null
+++ b/dom/events/test/test_bug1264380.html
@@ -0,0 +1,82 @@
+<html>
+<head>
+ <title>Test the dragstart event on the anchor in side shadow DOM</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+<script>
+
+async function runTests()
+{
+ let dragService = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
+ getService(SpecialPowers.Ci.nsIDragService);
+
+ let iframe = document.querySelector("iframe");
+ let iframeDoc = iframe.contentDocument;
+ let iframeWin = iframe.contentWindow;
+
+ let shadow = iframeDoc.querySelector('#outer').attachShadow({mode: 'open'});
+
+ let target = iframeDoc.createElement('a');
+ target.textContent = "Drag me if you can!";
+ const URL = "http://www.mozilla.org/";
+ target.href = URL;
+ shadow.appendChild(target);
+
+ // Some of the drag data we don't actually care about for this test,
+ // so we'll use this comparator function to ignore them.
+ function ignoreFunc(actualData, expectedData) {
+ return true;
+ }
+
+ const EXPECTED_DRAG_DATA = [[{
+ type: "text/x-moz-url",
+ data: "",
+ eqTest: ignoreFunc,
+ }, {
+ type: "text/x-moz-url-data",
+ data: "",
+ eqTest: ignoreFunc,
+ }, {
+ type: "text/x-moz-url-desc",
+ data: "",
+ eqTest: ignoreFunc,
+ }, {
+ type: "text/uri-list",
+ data: URL,
+ }, {
+ type: "text/_moz_htmlinfo",
+ data: "",
+ eqTest: ignoreFunc,
+ }, {
+ type: "text/html",
+ data: "",
+ eqTest: ignoreFunc,
+ }, {
+ type: "text/plain",
+ data: URL,
+ }]];
+
+ let result = await synthesizePlainDragAndCancel(
+ {
+ srcElement: target,
+ srcWindow: iframeWin,
+ finalY: -10, // Avoid clicking the link
+ },
+ EXPECTED_DRAG_DATA);
+ ok(result === true, "Should have gotten the expected drag data.");
+ SimpleTest.finish();
+}
+
+
+SimpleTest.waitForExplicitFinish();
+window.onload = () => {
+ SimpleTest.waitForFocus(runTests);
+};
+
+</script>
+
+<body>
+ <iframe srcdoc='<div id="outer"/>'></iframe>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1298970.html b/dom/events/test/test_bug1298970.html
new file mode 100644
index 0000000000..dc7c15a9d6
--- /dev/null
+++ b/dom/events/test/test_bug1298970.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1298970
+-->
+<head>
+ <title>Test for Bug 1298970</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=1298970">Mozilla Bug 1298970</a>
+<p id="display"></p>
+<div id="inner"></div>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1298970 **/
+var target = document.getElementById("inner");
+var event = new Event("test", { bubbles: true, cancelable: true });
+
+is(event.cancelBubble, false, "Event.cancelBubble should be false by default");
+
+target.addEventListener("test", (e) => {
+ e.stopPropagation();
+ is(e.cancelBubble, true, "Event.cancelBubble should be true after stopPropagation");
+}, true);
+
+target.dispatchEvent(event);
+
+</script>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug1304044.html b/dom/events/test/test_bug1304044.html
new file mode 100644
index 0000000000..39eb39df15
--- /dev/null
+++ b/dom/events/test/test_bug1304044.html
@@ -0,0 +1,133 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1304044
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1304044</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">
+ var eventsFired = [];
+ var target;
+ var eventsExpected;
+
+ function GetNodeString(node) {
+ if (node == window)
+ return "window";
+ if (node == document)
+ return "document";
+ if (node.id)
+ return node.id;
+ if (node.nodeName)
+ return node.nodeName;
+ return node;
+ }
+
+ function TargetAndListener(listener, targetInner) {
+ this.listener = listener;
+ this.target = targetInner;
+ }
+
+ TargetAndListener.prototype.toString = function() {
+ var targetName = GetNodeString(this.target);
+ var listenerName = GetNodeString(this.listener);
+ return "(listener: " + listenerName + ", target: " + targetName + ")";
+ }
+
+ var tests = [
+ TestAuxClickBubblesForEventListener,
+ TestAuxClickBubblesForOnAuxClick,
+ ];
+
+ function CompareEvents(evt, expected) {
+ return evt && expected && evt.listener == expected.listener &&
+ evt.target == expected.target;
+ }
+
+ function ResetEventsFired() {
+ eventsFired = [];
+ }
+
+ function ClearEventListeners() {
+ for (i in arguments) {
+ arguments[i].removeEventListener("auxclick", log_event);
+ }
+ }
+
+ function ClickTarget(tgt) {
+ synthesizeMouseAtCenter(tgt, {type : "mousedown", button: 2}, window);
+ synthesizeMouseAtCenter(tgt, {type : "mouseup", button: 2}, window);
+ }
+
+ function log_event(e) {
+ eventsFired[eventsFired.length] = new TargetAndListener(this, e.target);
+ }
+
+ function CompareEventsToExpected(expected, actual) {
+ for (var i = 0; i < expected.length || i < actual.length; i++) {
+ ok(CompareEvents(actual[i], expected[i]),
+ "Auxclick receiver's don't match: TargetAndListener " +
+ i + ": Expected: " + expected[i] + ", Actual: " + actual[i]);
+ }
+ }
+
+ function TestAuxClickBubblesForEventListener() {
+ target.addEventListener("auxclick", log_event);
+ document.addEventListener("auxclick", log_event);
+ window.addEventListener("auxclick", log_event);
+
+ ClickTarget(target)
+ CompareEventsToExpected(eventsExpected, eventsFired);
+ ResetEventsFired();
+ ClearEventListeners(target, document, window);
+ }
+
+ function TestAuxClickBubblesForOnAuxClick() {
+ target.onauxclick = log_event;
+ document.onauxclick = log_event;
+ window.onauxclick = log_event;
+
+ ClickTarget(target);
+ CompareEventsToExpected(eventsExpected, eventsFired);
+ ResetEventsFired();
+ }
+
+ function RunTests(){
+ for (var i = 0; i < tests.length; i++) {
+ tests[i]();
+ }
+ }
+
+ function Begin() {
+ target = document.getElementById("target");
+ eventsExpected = [
+ new TargetAndListener(target, target),
+ new TargetAndListener(document, target),
+ new TargetAndListener(window, target),
+ ];
+ RunTests();
+ target.remove();
+ SimpleTest.finish();
+ }
+
+ window.onload = function() {
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.executeSoon(Begin);
+ }
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1304044">Mozilla Bug 1304044</a>
+<p id="display">
+ <div id="target">Target</div>
+</p>
+<div id="content" style:"display:none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1305458.html b/dom/events/test/test_bug1305458.html
new file mode 100644
index 0000000000..a595502892
--- /dev/null
+++ b/dom/events/test/test_bug1305458.html
@@ -0,0 +1,50 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1305458
+-->
+<head>
+ <title>Test for Bug 1305458</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>
+ input[type=number] {
+ appearance: textfield;
+ }
+ input[type=number]:focus,
+ input[type=number]:hover {
+ appearance: auto;
+ }
+ </style>
+</head>
+<body onload="doTest()">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1305458">Mozilla Bug 1305458</a>
+ <input id="test_input" type="number">
+ <div id="test_div">bar</div>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ var change_count = 0;
+ function doTest() {
+ let input = document.getElementById("test_input");
+ let div = document.getElementById("test_div");
+ input.addEventListener("change", () => {
+ ++change_count;
+ });
+ // mouse hover
+ input.focus();
+ synthesizeMouse(input, 1, 1, {type: "mousemove"});
+ sendString("1");
+ input.blur();
+ is(change_count, 1, "input should fire change when blur");
+
+ input.focus();
+ synthesizeMouse(div, 1, 1, {type: "mousemove"});
+ sendString("1");
+ input.blur();
+ is(change_count, 2, "input should fire change when blur");
+ SimpleTest.finish();
+ }
+ </script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1327798.html b/dom/events/test/test_bug1327798.html
new file mode 100644
index 0000000000..ac903654f1
--- /dev/null
+++ b/dom/events/test/test_bug1327798.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Test for bug 1327798</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?=id=1327798">Mozilla Bug 1327798</a>
+<p id="display"></p>
+<div id="content" style="display: none;"></div>
+
+<div contenteditable="true" id="editable1"><b>Formatted Text</b><br></div>
+<pre>
+<script class="testbody" type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(() => {
+ var editable = document.getElementById("editable1");
+ editable.focus();
+
+ window.getSelection().selectAllChildren(editable1);
+
+ SpecialPowers.doCommand(window, "cmd_copy");
+
+ //--------- now check the content of the clipboard
+ var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
+ .getService(SpecialPowers.Ci.nsIClipboard);
+ // does the clipboard contain text/plain data ?
+ ok(clipboard.hasDataMatchingFlavors(["text/plain"], clipboard.kGlobalClipboard),
+ "clipboard contains unicode text");
+ // does the clipboard contain text/html data ?
+ ok(clipboard.hasDataMatchingFlavors(["text/html"], clipboard.kGlobalClipboard),
+ "clipboard contains html text");
+
+ window.addEventListener("paste", (e) => {
+ is(e.clipboardData.types.indexOf('text/html'), -1, "clipboardData shouldn't have text/html");
+ is(e.clipboardData.getData('text/plain'), "Formatted Text", "getData(text/plain) should return plain text");
+ SimpleTest.finish();
+ });
+
+ SpecialPowers.doCommand(window, "cmd_pasteNoFormatting");
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1332699.html b/dom/events/test/test_bug1332699.html
new file mode 100644
index 0000000000..c2a858d8ad
--- /dev/null
+++ b/dom/events/test/test_bug1332699.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for bug 1332699</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<style>
+#test {
+ color: red;
+ transition: color 100ms;
+}
+#test.changed {
+ color: green;
+}
+</style>
+<div id="test"></div>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+window.onload = function () {
+ let $test = document.getElementById('test');
+ is(getComputedStyle($test).color, 'rgb(255, 0, 0)',
+ 'color should be red before transition');
+ let numEvents = 0;
+ $test.addEventListener('webkitTransitionEnd', function() {
+ ++numEvents;
+ if (numEvents == 1) {
+ is(getComputedStyle($test).color, 'rgb(0, 128, 0)',
+ 'color should be green after transition');
+ $test.dispatchEvent(new TransitionEvent('transitionend'));
+ is(numEvents, 1, "Shouldn't receive the prefixed event again");
+ SimpleTest.finish();
+ }
+ });
+ $test.className = 'changed';
+};
+</script>
diff --git a/dom/events/test/test_bug1339758.html b/dom/events/test/test_bug1339758.html
new file mode 100644
index 0000000000..0a437f57de
--- /dev/null
+++ b/dom/events/test/test_bug1339758.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1339758
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1339758</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 1339758 **/
+ var expectNonZeroCoordinates = false;
+ SimpleTest.waitForExplicitFinish();
+
+ function testCoordinates(e) {
+ var coordinateProperties =
+ [ "screenX",
+ "screenY",
+ "clientX",
+ "clientY",
+ "offsetX",
+ "offsetY",
+ "movementX",
+ "movementY",
+ "layerX",
+ "layerY",
+ "x",
+ "y" ];
+ for (var i in coordinateProperties) {
+ if (e[coordinateProperties[i]] != 0) {
+ ok(expectNonZeroCoordinates, e.target.id + " got at least some non-zero coordinate property: " + i);
+ return;
+ }
+ }
+ ok(!expectNonZeroCoordinates, "Non-zero coordinates weren't expected");
+ }
+
+ function runTests() {
+ info("Testing click events which should have only 0 coordinates.");
+ document.getElementById("div_target").click();
+ document.getElementById("a_target").focus();
+ sendKey("RETURN");
+ document.getElementById("input_target").focus();
+ sendKey("RETURN");
+
+ info("Testing click events which should have also non-zero coordinates.");
+ expectNonZeroCoordinates = true;
+ // Test script created MouseEvents
+ sendMouseEvent({ type: "click"}, document.getElementById("a_target"));
+ sendMouseEvent({ type: "click"}, document.getElementById("input_target"));
+
+ // Test widget level mouse events
+ synthesizeMouse(document.getElementById("a_target"), 2, 2, {});
+ synthesizeMouse(document.getElementById("input_target"), 2, 2, {});
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForFocus(runTests);
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1339758">Mozilla Bug 1339758</a>
+<p id="display"></p>
+
+<div id="div_target" onclick="testCoordinates(event)">&nbsp;</div>
+<a href="#" id="a_target" onclick="testCoordinates(event);">test link</a><br>
+<input type="button" id="input_target" onclick="testCoordinates(event);" value="test button">
+
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1369072.html b/dom/events/test/test_bug1369072.html
new file mode 100644
index 0000000000..9f117c4902
--- /dev/null
+++ b/dom/events/test/test_bug1369072.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1369072
+-->
+<head>
+ <title>Test for Bug 1369072</title>
+ <script type="text/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=1369072">Mozilla Bug 1369072</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 1369072 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var subWin = window.open("window_bug1369072.html", "_blank",
+ "width=500,height=500");
+
+function finish()
+{
+ subWin.close();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1412775.xhtml b/dom/events/test/test_bug1412775.xhtml
new file mode 100644
index 0000000000..3146a489f1
--- /dev/null
+++ b/dom/events/test/test_bug1412775.xhtml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1412775
+-->
+<window title="Mozilla Bug 1412775"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="init()">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/chrome-harness.js"></script>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+
+ /** Test for Bug 1412775 **/
+ var win;
+ function init() {
+ SimpleTest.waitForExplicitFinish();
+ win = window.browsingContext.topChromeWindow.open("window_bug1412775.xhtml", "_new", "chrome");
+ win.onload = function() {
+ var b = win.document.getElementById("browser");
+ var d = b.contentWindow.document;
+ var e = new d.defaultView.Event("foo");
+ var didCallChromeSide = false;
+ var didCallContentSide = false;
+ /* eslint-disable-next-line no-shadow */
+ b.addEventListener("foo", function(e) {
+ didCallChromeSide = true;
+ var path = e.composedPath();
+ var mm = d.defaultView.docShell.messageManager;
+ is(path.length, 5, "Should have 5 items in composedPath in chrome.");
+ is(path[0], mm, "BrowserChildGlobal is the chrome handler.");
+ is(path[1], b, "browser element should be in the path.");
+ is(path[2], b.parentNode, "window element should be in the path.");
+ is(path[3], win.document, "Document object should be in the path.");
+ is(path[4], win, "Window object should be in the path.");
+ }, true, true);
+ /* eslint-disable-next-line no-shadow */
+ d.addEventListener("foo", function(e) {
+ didCallContentSide = true;;
+ var path = e.composedPath();
+ is(path.length, 4, "Should have 4 items in composedPath in content.");
+ is(path[0], d.body, "body element should be in the path.");
+ is(path[1], d.documentElement, "html element should be in the path.");
+ is(path[2], d, "Document object should be in the path.");
+ is(path[3], d.defaultView, "Window object should be in the path.");
+ }, true, true);
+ d.body.dispatchEvent(e);
+ ok(didCallChromeSide, "didCallChromeSide");
+ ok(didCallContentSide, "didCallContentSide");
+ win.close();
+ SimpleTest.finish();
+ }
+ }
+
+ ]]>
+ </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=1412775"
+ target="_blank">Mozilla Bug 1412775</a>
+ </body>
+</window>
diff --git a/dom/events/test/test_bug1429572.html b/dom/events/test/test_bug1429572.html
new file mode 100644
index 0000000000..c9a5c0cc0a
--- /dev/null
+++ b/dom/events/test/test_bug1429572.html
@@ -0,0 +1,43 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1429572
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1429572</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 1429572 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+ function start() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", 1],
+ ["dom.w3c_touch_events.legacy_apis.enabled", true]]},
+ function() {
+ ok(true, "Starting the test.");
+ win = window.open("window_bug1429572.html", "testwindow",
+ "width=" + window.screen.width +
+ ",height=" + window.screen.height);
+ });
+ }
+
+ function done() {
+ setTimeout(SimpleTest.finish);
+ }
+
+ </script>
+</head>
+<body onload="start();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1429572">Mozilla Bug 1429572</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1446834.html b/dom/events/test/test_bug1446834.html
new file mode 100644
index 0000000000..495f9b4b3c
--- /dev/null
+++ b/dom/events/test/test_bug1446834.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1446834
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1446834</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 1446834 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = function() {
+ document.getElementById("iframe").src = "file_bug1446834.html";
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1446834">Mozilla Bug 1446834</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<iframe id="iframe"></iframe>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1447993.html b/dom/events/test/test_bug1447993.html
new file mode 100644
index 0000000000..9e77ac06c9
--- /dev/null
+++ b/dom/events/test/test_bug1447993.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1447993
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1447993</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 1447993 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var win;
+ function start() {
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", 1]]},
+ function() {
+ win = window.open("window_bug1447993.html", "testwindow",
+ "width=" + window.screen.width +
+ ",height=" + window.screen.height);
+ });
+ }
+
+ function done() {
+ setTimeout(SimpleTest.finish);
+ }
+
+ </script>
+</head>
+<body onload="start();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1447993">Mozilla Bug 1447993</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1484371.html b/dom/events/test/test_bug1484371.html
new file mode 100644
index 0000000000..c1b2a31217
--- /dev/null
+++ b/dom/events/test/test_bug1484371.html
@@ -0,0 +1,27 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1484371
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1484371</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 1484371 **/
+
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = function() {
+ document.getElementById("iframe").src = "file_bug1484371.html";
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1484371">Mozilla Bug 1484371</a>
+<iframe id="iframe"></iframe>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1534562.html b/dom/events/test/test_bug1534562.html
new file mode 100644
index 0000000000..7ac6c31dd7
--- /dev/null
+++ b/dom/events/test/test_bug1534562.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1534562
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1534562</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 1534562 **/
+
+ function runTest() {
+ var host = document.getElementById("host");
+ var shadow = host.attachShadow({mode: 'open'});
+ var shadowDiv = document.createElement('div');
+ shadowDiv.style.cssText = "height: 100%; width: 100%";
+ shadowDiv.onpointerdown = function (e) {
+ shadowDiv.setPointerCapture(e.pointerId);
+ };
+ var shadowDivGotPointerMove = false;
+ shadowDiv.onpointermove = function(e) {
+ shadowDivGotPointerMove = true;
+ }
+ shadow.appendChild(shadowDiv);
+ host.offsetLeft; // Flush layout.
+
+ synthesizeMouseAtCenter(shadowDiv, { type: "mousedown" });
+ synthesizeMouseAtCenter(document.getElementById("lightDOM"), { type: "mousemove" });
+ ok(shadowDivGotPointerMove, "shadowDiv should have got pointermove event.");
+ synthesizeMouseAtCenter(document.getElementById("lightDOM"), { type: "mouseup" });
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(runTest);
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1534562">Mozilla Bug 1534562</a>
+<div id="host" style="height: 50px; width: 50px;">
+</div>
+<div id="lightDOM" style="height: 50px; width: 50px;">
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1539497.html b/dom/events/test/test_bug1539497.html
new file mode 100644
index 0000000000..4f49f1a044
--- /dev/null
+++ b/dom/events/test/test_bug1539497.html
@@ -0,0 +1,29 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>bug 1539497</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function runTest() {
+ var win = window.open("about:blank");
+ win.onload = function() {
+ is(win.navigator.maxTouchPoints, 5, "Should have max touch points");
+ win.close();
+ SimpleTest.finish();
+ }
+ }
+ function init() {
+ SpecialPowers.pushPrefEnv(
+ {"set": [["dom.maxtouchpoints.testing.value", 5]]}, runTest);
+ }
+ </script>
+</head>
+<body onload="init()">
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1581192.html b/dom/events/test/test_bug1581192.html
new file mode 100644
index 0000000000..3a3a2ab322
--- /dev/null
+++ b/dom/events/test/test_bug1581192.html
@@ -0,0 +1,73 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Redispatching test with PresShell</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<button>click me!</button>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ /**
+ * We have same tests in Event-dispatch-redispatch.html of WPT. However,
+ * it does not send the event to the main process. Therefore the reported
+ * crash couldn't reproduce.
+ */
+ await SpecialPowers.pushPrefEnv({set: [["test.events.async.enabled", true]]});
+ let button = document.querySelector("button");
+ try {
+ await promiseElementReadyForUserInput(button, window, info);
+ } catch (ex) {
+ ok(false, ex.message);
+ SimpleTest.finish();
+ return;
+ }
+ let mouseupEvent;
+ button.addEventListener("mouseup", aNativeMouseUpEvent => {
+ ok(aNativeMouseUpEvent.isTrusted,"First mouseup event should be trusted");
+ mouseupEvent = aNativeMouseUpEvent;
+ try {
+ button.dispatchEvent(aNativeMouseUpEvent);
+ ok(false, "Dispatching trusted mouseup event which is being dispatched should throw an exception");
+ } catch (e) {
+ is(e.name, "InvalidStateError", "Trusted mouseup event which is being dispatched shouldn't be able to be dispatched");
+ }
+ }, {once: true});
+
+ button.addEventListener("click", aNativeClickEvent => {
+ ok(aNativeClickEvent.isTrusted, "First click event should be trusted");
+ try {
+ button.dispatchEvent(aNativeClickEvent);
+ ok(false, "Dispatching trusted click event which is being dispatched should throw an exception");
+ } catch (e) {
+ is(e.name, "InvalidStateError", "Trusted click event which is being dispatched shouldn't be able to be dispatched");
+ }
+ let mouseupEventFired = false;
+ button.addEventListener("mouseup", aDispatchedMouseUpEvent => {
+ ok(!aDispatchedMouseUpEvent.isTrusted, "Redispatched mouseup event shouldn't be trusted");
+ mouseupEventFired = true;
+ }, {once: true});
+ function onClick(aNonDispatchedClickEvent) {
+ ok(false, "Redispatched mouseup event shouldn't cause dispatching another click event");
+ }
+ button.addEventListener("click", onClick);
+ ok(mouseupEvent.isTrusted, "Received mouseup event should be trusted before redispatching from click event listener");
+ button.dispatchEvent(mouseupEvent);
+ ok(!mouseupEvent.isTrusted, "Received mouseup event shouldn't be trusted after redispatching");
+ ok(mouseupEventFired, "Redispatched mouseup event should've been received");
+ button.removeEventListener("click", onClick);
+ ok(aNativeClickEvent.isTrusted, "First click event should still be trusted even after redispatching mouseup event");
+ SimpleTest.finish();
+ }, {once: true});
+ synthesizeMouseAtCenter(button, {});
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1635018.html b/dom/events/test/test_bug1635018.html
new file mode 100644
index 0000000000..ebfbce4abe
--- /dev/null
+++ b/dom/events/test/test_bug1635018.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for Bug 1709832</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+<div id="container">
+ <span id="start" draggable="true">start</span><span id="dest" draggable="true">dest</span>
+</div>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ SpecialPowers.pushPrefEnv({
+ set: [["dom.event.dragexit.enabled", false]]
+ }).then(() => {
+ start.addEventListener("dragexit", ev => {
+ ok(false, "dragexit should not fire at non-chrome element")
+ });
+ start.addEventListener("dragleave", ev => {
+ ok(true, "got dragleave")
+ SimpleTest.finish();
+ });
+
+ sendDragEvent({ type: "dragover" }, start);
+ sendDragEvent({ type: "dragover" }, dest);
+ });
+</script>
diff --git a/dom/events/test/test_bug1637259.html b/dom/events/test/test_bug1637259.html
new file mode 100644
index 0000000000..e6e0a4bd38
--- /dev/null
+++ b/dom/events/test/test_bug1637259.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1692277</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<style>
+ #container {
+ width: 100px;
+ height: 100px;
+ overflow: scroll;
+ }
+
+ #child {
+ width: 200px;
+ height: 200px;
+ }
+</style>
+
+<div id="container">
+ <div id="child"></div>
+</div>
+
+<script>
+ /**
+ * @template {keyof HTMLElementEventMap} K
+ * @param {HTMLElemnt} target
+ * @param {K} eventName
+ * @return {HTMLElementEventMap[K]}
+ */
+ function waitForEvent(target, eventName) {
+ return new Promise(resolve => {
+ target.addEventListener(eventName, resolve, { once: true });
+ });
+ }
+
+ add_task(async function testPenDrag() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.w3c_pointer_events.dispatch_by_pointer_messages", true],
+ ["dom.w3c_pointer_events.scroll_by_pen.enabled", true],
+ ],
+ });
+
+ await SimpleTest.promiseFocus();
+ const container = document.getElementById("container");
+ const scrollPromise = waitForEvent(container, "scroll");
+ const pointerPromise = waitForEvent(container, "pointerdown");
+ await promiseNativePointerDrag(container, "pen", 50, 50, -50, -50, 0, {
+ pressure: 0.25,
+ tiltX: 40,
+ tiltY: 50,
+ twist: 80,
+ });
+ await scrollPromise;
+
+ const pointerdown = await pointerPromise;
+ is(pointerdown.pointerType, "pen");
+ is(pointerdown.type, "pointerdown", ".type");
+ is(pointerdown.button, 0, ".button");
+ is(pointerdown.buttons, 1, ".buttons");
+ is(pointerdown.layerX, 50, ".layerX");
+ is(pointerdown.layerY, 50, ".layerY");
+ is(pointerdown.pressure, 0.25, ".pressure");
+ is(pointerdown.tiltX, 40, ".tiltX");
+ is(pointerdown.tiltY, 50, ".tiltY");
+ is(pointerdown.twist, 80, ".twist");
+ });
+</script>
diff --git a/dom/events/test/test_bug1673434.html b/dom/events/test/test_bug1673434.html
new file mode 100644
index 0000000000..f9bb09c69e
--- /dev/null
+++ b/dom/events/test/test_bug1673434.html
@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<!--
+bugzilla.mozilla.org/show_bug.cgi?id=1673434
+-->
+<head>
+<title>Test for bug 1673434</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1673434">Mozilla Bug 1673434</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<input type="checkbox">
+<input type="radio" name="group" value="foo">
+<input type="radio" name="group" value="bar" checked>
+<input type="text">
+<script>
+const utils = SpecialPowers.DOMWindowUtils;
+
+function test_events(element, resolve) {
+ element.addEventListener("input", () => {
+ is(utils.isHandlingUserInput, false,
+ "isHandlingUserInput is false on input event by element.click()");
+ }, { once: true });
+ element.addEventListener("change", () => {
+ is(utils.isHandlingUserInput, false,
+ "isHandlingUserInput is false on change event by element.click()");
+ resolve();
+ }, { once: true });
+
+ element.click();
+}
+
+add_task(function testCheckboxEvent() {
+ return new Promise(resolve => {
+ let element = document.querySelector("input[type=checkbox]");
+ test_events(element, resolve);
+ });
+});
+
+add_task(function testRadioEvent() {
+ return new Promise(resolve => {
+ let element = document.querySelector("input[type=radio]");
+ test_events(element, resolve);
+ });
+});
+
+add_task(function testUserInput() {
+ // setUserInput should be handled as user input.
+ //
+ // XXX <textarea> won't fire input event by setUserInput.
+ return new Promise(resolve => {
+ let element = document.querySelector("input[type=text]");
+ element.addEventListener("input", () => {
+ is(utils.isHandlingUserInput, true,
+ "isHandlingUserInput is true on input event by setUserInput");
+ }, { once: true });
+ element.addEventListener("change", () => {
+ is(utils.isHandlingUserInput, true,
+ "isHandlingUserInput is true on change event by setUserInput");
+ resolve();
+ }, { once: true });
+
+ SpecialPowers.wrap(element).setUserInput("a");
+ });
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1681800.html b/dom/events/test/test_bug1681800.html
new file mode 100644
index 0000000000..f9e73fb397
--- /dev/null
+++ b/dom/events/test/test_bug1681800.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+<!--
+bugzilla.mozilla.org/show_bug.cgi?id=1681800
+-->
+<head>
+<title>Test for bug 1681800</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=1681800">Mozilla Bug 1681800</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script>
+add_task(function() {
+ document.documentElement.focus({})
+ document.documentElement.focus({})
+ ok(true, "no crash");
+})
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1686716.html b/dom/events/test/test_bug1686716.html
new file mode 100644
index 0000000000..8779413a1a
--- /dev/null
+++ b/dom/events/test/test_bug1686716.html
@@ -0,0 +1,28 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>bug 1686716</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ function test() {
+ var ifr = document.getElementsByTagName("iframe")[0];
+ ifr.contentWindow.addEventListener("drop",
+ function(event) {
+ ifr.remove();
+ event.preventDefault();
+ });
+ sendDragEvent({type: "drop"}, ifr.contentDocument.body, ifr.contentWindow);
+ ok(true, "Should not crash.");
+ SimpleTest.finish();
+ }
+ </script>
+</head>
+<body onload="test()">
+<iframe></iframe>
+<p id="display"></p>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1692052.html b/dom/events/test/test_bug1692052.html
new file mode 100644
index 0000000000..da7a9810d8
--- /dev/null
+++ b/dom/events/test/test_bug1692052.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1692052
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1692052</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <script type="application/javascript">
+ SimpleTest.expectAssertions(0, 6); // 6 times runs
+ SimpleTest.waitForExplicitFinish();
+
+ window.onload = function() {
+ window.open("file_bug1692052.html?c=0");
+ ok(true, "Test window is opened");
+ }
+
+ function finishTests() {
+ ok(true, "Test window doesn't crash");
+ SimpleTest.finish();
+ }
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1692052">Mozilla Bug 1692052</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug1692277.html b/dom/events/test/test_bug1692277.html
new file mode 100644
index 0000000000..4615c4c48b
--- /dev/null
+++ b/dom/events/test/test_bug1692277.html
@@ -0,0 +1,43 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1692277</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+<style>
+ @font-face {
+ font-family: Ahem;
+ src: url("/tests/dom/base/test/Ahem.ttf");
+ }
+
+ #anchor {
+ font: 16px/1 Ahem;
+ }
+</style>
+
+<div>
+ <a href="#" id="anchor">A draggable link</a>
+</div>
+
+<script>
+ function waitForEvent(target, eventName) {
+ return new Promise(resolve => {
+ target.addEventListener(eventName, resolve, { once: true });
+ });
+ }
+
+ add_task(async function testPenDrag() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.w3c_pointer_events.dispatch_by_pointer_messages", true]],
+ });
+
+ await SimpleTest.promiseFocus();
+ const anchor = document.getElementById("anchor");
+ await promiseNativePointerDrag(anchor, "pen", 5, 5, 50, 50);
+ const promise = waitForEvent(anchor, "click");
+ await promiseNativePointerDrag(anchor, "pen", 5, 5, 5, 5);
+ await promise;
+ ok(true, "Got the click event");
+ });
+</script>
diff --git a/dom/events/test/test_bug1709832.html b/dom/events/test/test_bug1709832.html
new file mode 100644
index 0000000000..b277c4e0fb
--- /dev/null
+++ b/dom/events/test/test_bug1709832.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for Bug 1709832</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+<style>
+ @font-face {
+ font-family: Ahem;
+ src: url("/tests/dom/base/test/Ahem.ttf");
+ }
+
+ #container {
+ font: 16px/1 Ahem;
+ }
+</style>
+<div id="container">
+ <span id="start" draggable="true">start</span><span id="dest" draggable="true">dest</span>
+</div>
+<script>
+ SimpleTest.waitForExplicitFinish();
+
+ dest.addEventListener("dragenter", ev => {
+ is(ev.target, dest, "dragenter target should be element");
+ is(ev.relatedTarget, start, "dragenter relatedTarget should be element");
+ });
+ start.addEventListener("dragleave", ev => {
+ is(ev.target, start, "dragleave target should be element");
+ is(ev.relatedTarget, dest, "dragleave relatedTarget should be element");
+ SimpleTest.finish();
+ });
+
+ synthesizeMouse(start, 5, 5, { type: "mousedown" });
+
+ const utils = SpecialPowers.getDOMWindowUtils(window);
+
+ // Intentionally passing a text node to test EventStateManager behavior
+ // when GetContentForEvent gives a text node
+ // EventUtils.sendDragEvent is unusable because it requires an element as a target
+ utils.dispatchDOMEventViaPresShellForTesting(start.childNodes[0], new DragEvent("dragover", {
+ ...createDragEventObject("dragover", start, window, null, {})
+ }));
+ utils.dispatchDOMEventViaPresShellForTesting(dest.childNodes[0], new DragEvent("dragover", {
+ ...createDragEventObject("dragover", dest, window, null, {})
+ }));
+</script>
diff --git a/dom/events/test/test_bug1710509.html b/dom/events/test/test_bug1710509.html
new file mode 100644
index 0000000000..4454a20b9e
--- /dev/null
+++ b/dom/events/test/test_bug1710509.html
@@ -0,0 +1,49 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1710509</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<style>
+ #container {
+ width: 100px;
+ height: 100px;
+ touch-action: none;
+ }
+</style>
+
+<div id="container"></div>
+
+<script>
+ /**
+ * @template {keyof HTMLElementEventMap} K
+ * @param {HTMLElemnt} target
+ * @param {K} eventName
+ * @return {HTMLElementEventMap[K]}
+ */
+ function waitForEvent(target, eventName) {
+ return new Promise(resolve => {
+ target.addEventListener(eventName, resolve, { once: true });
+ });
+ }
+
+ add_task(async function testPenDrag() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.w3c_pointer_events.dispatch_by_pointer_messages", false],
+ ],
+ });
+
+ await SimpleTest.promiseFocus();
+ const container = document.getElementById("container");
+ const touchMovePromise = waitForEvent(container, "touchmove");
+ await promiseNativePointerDrag(container, "pen", 50, 50, -50, -50);
+
+ const touchmove = await touchMovePromise;
+ const [touch] = touchmove.touches;
+ is(touch.radiusX, 1, ".radiusX");
+ is(touch.radiusY, 1, ".radiusX");
+ });
+</script>
diff --git a/dom/events/test/test_bug1728171.html b/dom/events/test/test_bug1728171.html
new file mode 100644
index 0000000000..e6136f06d4
--- /dev/null
+++ b/dom/events/test/test_bug1728171.html
@@ -0,0 +1,70 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Bug 1728171</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+
+<style>
+ #container {
+ width: 100px;
+ height: 100px;
+ }
+</style>
+
+<div id="container"></div>
+
+<script>
+ const container = document.getElementById("container");
+
+ add_task(async function testPenContextMenu() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.w3c_pointer_events.dispatch_by_pointer_messages", true],
+ ...getPrefs("TOUCH_EVENTS:PAN"),
+ ],
+ });
+
+ await waitUntilApzStable();
+
+ let pointerUpSeen = false
+ container.addEventListener("pointerup", () => {
+ pointerUpSeen = true;
+ }, { once: true });
+ const contextMenuPromise = promiseOneEvent(container, "contextmenu");
+ await promiseNativePointerTap(container, "pen", 50, 50, { button: 2 });
+
+ const contextmenu = await contextMenuPromise;
+ ok(pointerUpSeen, "pointerup event was seen");
+ is(contextmenu.button, 2, ".button indicates secondary button");
+
+ // Close the context menu.
+ await promiseNativePointerTap(container, "pen", 30, 30);
+ });
+
+ add_task(async function testPenContextMenuWhenButtonChange() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.w3c_pointer_events.dispatch_by_pointer_messages", true],
+ ...getPrefs("TOUCH_EVENTS:PAN"),
+ ],
+ });
+
+ await waitUntilApzStable();
+
+ const contextMenuPromise = promiseOneEvent(container, "contextmenu");
+
+ await promiseNativePointerInput(container, "pen", SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, 50, 50, { button: 2 });
+ // no button
+ await promiseNativePointerInput(container, "pen", SpecialPowers.DOMWindowUtils.TOUCH_CONTACT, 50, 50);
+ await promiseNativePointerInput(container, "pen", SpecialPowers.DOMWindowUtils.TOUCH_REMOVE, 50, 50);
+
+ const contextmenu = await contextMenuPromise;
+ is(contextmenu.button, 2, ".button still indicates secondary button");
+
+ // Close the context menu.
+ await promiseNativePointerTap(container, "pen", 30, 30);
+ });
+</script>
diff --git a/dom/events/test/test_bug226361.xhtml b/dom/events/test/test_bug226361.xhtml
new file mode 100644
index 0000000000..143a485757
--- /dev/null
+++ b/dom/events/test/test_bug226361.xhtml
@@ -0,0 +1,82 @@
+<html xmlns="http://www.w3.org/1999/xhtml">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=226361
+-->
+<head>
+ <title>Test for Bug 226361</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 id="body1">
+<p id="display">
+
+ <a id="b1" tabindex="1" href="http://home.mozilla.org">start</a><br />
+<br />
+
+<iframe id="iframe" tabindex="2" src="bug226361_iframe.xhtml"></iframe>
+
+ <a id="b2" tabindex="3" href="http://home.mozilla.org">end</a>
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 226361 **/
+
+// accessibility.tabfocus must be set to value 7 before running test also
+// on a mac.
+function setTabFocus() {
+ SpecialPowers.pushPrefEnv({ set: [[ "accessibility.tabfocus", 7 ]] }, doTest);
+}
+
+// =================================
+
+var doc = document;
+function tab_to(id) {
+ synthesizeKey("KEY_Tab", {});
+ is(doc.activeElement.id, id, "element with id=" + id + " should have focus");
+}
+
+function tab_iframe() {
+ doc = document;
+ tab_to('iframe');
+
+ // inside iframe
+ doc = document.getElementById('iframe').contentDocument
+ tab_to('a3');tab_to('a5');tab_to('a1');tab_to('a2');tab_to('a4');
+}
+
+
+function doTest() {
+ window.getSelection().removeAllRanges();
+ document.getElementById('body1').focus();
+ is(document.activeElement.id, document.body.id, "body element should be focused");
+
+ doc = document;
+ tab_to('b1');
+
+ tab_iframe();
+
+ doc=document
+ document.getElementById('iframe').focus()
+ tab_to('b2');
+ // Change tabindex so the next TAB goes back to the IFRAME
+ document.getElementById('iframe').setAttribute('tabindex','4');
+
+ tab_iframe();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(setTabFocus);
+
+]]>
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug238987.html b/dom/events/test/test_bug238987.html
new file mode 100644
index 0000000000..b2712a25d0
--- /dev/null
+++ b/dom/events/test/test_bug238987.html
@@ -0,0 +1,280 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=238987
+-->
+<head>
+ <title>Test for Bug 238987</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=238987">Mozilla Bug 238987</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Test for Bug 238987 **/
+
+ var shouldStop = false;
+ var activateShift = false;
+ var expectedResult = "i1,i2,i3,i4,i5,i6,i7,i8,number,i9,i10,i11,i12";
+ var forwardFocusArray = expectedResult.split(",");
+ var backwardFocusArray = expectedResult.split(",");
+ var forwardBlurArray = expectedResult.split(",");
+ var backwardBlurArray = expectedResult.split(",");
+ // Adding 3 for "begin", "end", "begin" and one for the <a> in the Mochitest template,
+ var expectedWindowFocusCount = forwardFocusArray.length + backwardFocusArray.length + 4;
+ // but the last blur event goes to i1, not "begin".
+ var expectedWindowBlurCount = forwardFocusArray.length + backwardFocusArray.length + 3;
+
+ function handleFocus(e) {
+ if (e.target.id == "begin") {
+ // if the activateShift is set, the test is coming back from the end.
+ if (activateShift) {
+ shouldStop = true;
+ }
+ } else if (e.target.id == "end") {
+ activateShift = true;
+ } else if (activateShift) {
+ var expected = backwardFocusArray.pop();
+ ok(expected == e.target.id,
+ "(focus) Backward tabbing, expected [" +
+ expected + "], got [" + e.target.id + "]");
+ } else {
+ var expected = forwardFocusArray.shift();
+ is(e.target, document.activeElement, "Wrong activeElement!");
+ ok(expected == e.target.id,
+ "(focus) Forward tabbing, expected [" +
+ expected + "], got [" + e.target.id + "]");
+ }
+ }
+
+ function handleWindowFocus(e) {
+ --expectedWindowFocusCount;
+ var s = "target " + e.target;
+ if ("id" in e.target) {
+ s = s + ", id=\"" + e.target.id + "\"";
+ }
+ ok(e.eventPhase == Event.CAPTURING_PHASE,
+ "|window| should not have got a focus event, " + s);
+ }
+
+ function handleBlur(e) {
+ if (e.target.id == "begin" || e.target.id == "end") {
+ return;
+ }
+ if (activateShift) {
+ var expected = backwardBlurArray.pop();
+ ok(expected == e.target.id,
+ "(blur) backward tabbing, expected [" +
+ expected + "], got [" + e.target.id + "]");
+ } else {
+ var expected = forwardBlurArray.shift();
+ ok(expected == e.target.id,
+ "(blur) forward tabbing, expected [" +
+ expected + "], got [" + e.target.id + "]");
+ }
+ }
+
+ function handleWindowBlur(e) {
+ --expectedWindowBlurCount;
+ var s = "target " + e.target;
+ if ("id" in e.target) {
+ s = s + ", id=\"" + e.target.id + "\"";
+ }
+ ok(e.eventPhase == Event.CAPTURING_PHASE,
+ "|window| should not have got a blur event, " + s);
+ }
+
+ function tab() {
+ // Send tab key events.
+ synthesizeKey("KEY_Tab", {shiftKey: activateShift});
+ if (shouldStop) {
+ // Did focus handling succeed
+ is(forwardFocusArray.length, 0,
+ "Not all forward tabbing focus tests were run, " +
+ forwardFocusArray.toString());
+ is(backwardFocusArray.length, 0,
+ "Not all backward tabbing focus tests were run, " +
+ backwardFocusArray.toString());
+ is(expectedWindowFocusCount, 0,
+ "|window| didn't get the right amount of focus events");
+
+ // and blur.
+ is(forwardBlurArray.length, 0,
+ "Not all forward tabbing blur tests were run, " +
+ forwardBlurArray.toString());
+ is(backwardBlurArray.length, 0,
+ "Not all backward tabbing blur tests were run, " +
+ backwardBlurArray.toString());
+ is(expectedWindowBlurCount, 0,
+ "|window| didn't get the right amount of blur events");
+
+ // Cleanup
+ window.removeEventListener("focus", handleWindowFocus, true);
+ window.removeEventListener("focus", handleWindowFocus);
+ window.removeEventListener("blur", handleWindowBlur, true);
+ window.removeEventListener("blur", handleWindowBlur);
+ var elements = document.getElementsByTagName("*");
+ for (var i = 0; i < elements.length; ++i) {
+ if (elements[i].hasAttribute("id")) {
+ elements[i].removeEventListener("focus", handleFocus);
+ elements[i].removeEventListener("blur", handleBlur);
+ }
+ }
+
+ SimpleTest.finish();
+ } else {
+ setTimeout(tab, 0);
+ }
+ }
+
+ function start() {
+ window.focus();
+ window.addEventListener("focus", handleWindowFocus, true);
+ window.addEventListener("focus", handleWindowFocus);
+ window.addEventListener("blur", handleWindowBlur, true);
+ window.addEventListener("blur", handleWindowBlur);
+ var elements = document.getElementsByTagName("*");
+ for (var i = 0; i < elements.length; ++i) {
+ if (elements[i].hasAttribute("id")) {
+ elements[i].addEventListener("focus", handleFocus);
+ elements[i].addEventListener("blur", handleBlur);
+ }
+ if (elements[i].getAttribute("tabindex") == "1") {
+ elements[i].setAttribute("tabindex", "-1");
+ }
+ }
+ tab();
+ }
+
+ // accessibility.tabfocus must be set to value 7 before running test also
+ // on a mac.
+ function doTest() {
+ SpecialPowers.pushPrefEnv({"set": [["accessibility.tabfocus", 7]]}, start);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+
+</script>
+</pre>
+ <h4 tabindex="0" id="begin">Test:</h4>
+ <table>
+ <tbody>
+ <tr>
+ <td>type="text"</td><td><input type="text" id="i1" value=""></td>
+ </tr>
+ <tr>
+ <td>type="button"</td><td><input type="button" id="i2" value="type='button'"></td>
+ </tr>
+ <tr>
+ <td>type="checkbox"</td><td><input type="checkbox" id="i3" ></td>
+ </tr>
+ <tr>
+ <td>type="radio" checked</td><td><input type="radio" id="i4" name="radio" checked>
+ <input type="radio" id="i4b" name="radio"></td>
+ </tr>
+ <tr>
+ <td>type="radio"</td><td><input type="radio" id="i5" name="radio2">
+ <input type="radio" id="i6" name="radio2"></td>
+ </tr>
+ <tr>
+ <td>type="password"</td><td><input type="password" id="i7"></td>
+ </tr>
+ <tr>
+ <td>type="file"</td><td><input type="file" id="i8"></td>
+ </tr>
+ <tr>
+ <td>type="number"</td><td><input type="number" id="number"></td>
+ </tr>
+ <tr>
+ <td>button</td><td><button id="i9">button</button></td>
+ </tr>
+ <tr>
+ <td>select</td><td><select id="i10"><option>select</option></select></td>
+ </tr>
+ <tr>
+ <td>a</td><td><a href="#radio" id="i11">a link</a></td>
+ </tr>
+ <tr>
+ <td>tabindex="0"</td><td><span tabindex="0" id="i12">span</span></td>
+ </tr>
+
+ <tr>
+ <td><h3>Form elements with tabindex="-1"</h3></td>
+ </tr>
+ <tr>
+ <td>type="text"</td><td><input type="text" tabindex="-1" value=""></td>
+ </tr>
+ <tr>
+ <td>type="button"</td><td><input type="button" tabindex="-1" value="type='button'"></td>
+ </tr>
+ <tr>
+ <td>type="checkbox"</td><td><input type="checkbox" tabindex="-1"></td>
+ </tr>
+ <tr>
+ <td>type="radio" checked</td><td><input type="radio" tabindex="-1" name="radio3" checked>
+ <input type="radio" tabindex="-1" name="radio3"></td>
+ </tr>
+ <tr>
+ <td>type="radio"</td><td><input type="radio" tabindex="-1" name="radio4">
+ <input type="radio" tabindex="-1" name="radio4"></td>
+ </tr>
+ <tr>
+ <td>type="password"</td><td><input type="password" tabindex="-1"></td>
+ </tr>
+ <tr>
+ <td>type="file"</td><td><input type="file" tabindex="-1"></td>
+ </tr>
+ <tr>
+ <td>button</td><td><button tabindex="-1">button</button></td>
+ </tr>
+ <tr>
+ <td>select</td><td><select tabindex="-1"><option>select</option></select></td>
+ </tr>
+
+ <tr>
+ <td><h3>Form elements with .setAttribute("tabindex", "-1")</h3></td>
+ </tr>
+ <tr>
+ <td>type="text"</td><td><input type="text" tabindex="1" value=""></td>
+ </tr>
+ <tr>
+ <td>type="button"</td><td><input type="button" tabindex="1" value="type='button'"></td>
+ </tr>
+ <tr>
+ <td>type="checkbox"</td><td><input type="checkbox" tabindex="1"></td>
+ </tr>
+ <tr>
+ <td>type="radio" checked</td><td><input type="radio" tabindex="1" name="radio5" checked>
+ <input type="radio" tabindex="1" name="radio5"></td>
+ </tr>
+ <tr>
+ <td>type="radio"</td><td><input type="radio" tabindex="1" name="radio6">
+ <input type="radio" tabindex="1" name="radio6"></td>
+ </tr>
+ <tr>
+ <td>type="password"</td><td><input type="password" tabindex="1"></td>
+ </tr>
+ <tr>
+ <td>type="file"</td><td><input type="file" tabindex="1"></td>
+ </tr>
+ <tr>
+ <td>button</td><td><button tabindex="1">button</button></td>
+ </tr>
+ <tr>
+ <td>select</td><td><select tabindex="1"><option>select</option></select></td>
+ </tr>
+
+ </tbody>
+ </table>
+ <h4 tabindex="0" id="end">done.</h4>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug288392.html b/dom/events/test/test_bug288392.html
new file mode 100644
index 0000000000..f50327eed3
--- /dev/null
+++ b/dom/events/test/test_bug288392.html
@@ -0,0 +1,103 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=288392
+-->
+<head>
+ <title>Test for Bug 288392</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=288392">Mozilla Bug 288392</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<div id="mutationTarget">
+</div>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 288392 **/
+var subtreeModifiedCount;
+
+function subtreeModified(e)
+{
+ ++subtreeModifiedCount;
+}
+
+function doTest() {
+ var targetNode = document.getElementById("mutationTarget");
+ targetNode.addEventListener("DOMSubtreeModified", subtreeModified);
+
+ subtreeModifiedCount = 0;
+ var temp = document.createElement("DIV");
+ targetNode.appendChild(temp);
+ is(subtreeModifiedCount, 1,
+ "Appending a child node should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ temp.setAttribute("foo", "bar");
+ is(subtreeModifiedCount, 1,
+ "Setting an attribute should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ targetNode.removeChild(temp);
+ is(subtreeModifiedCount, 1,
+ "Removing a child node should have dispatched a DOMSubtreeModified event");
+
+ // Testing events in a subtree, which is not in the document.
+ var subtree = document.createElement("div");
+ var s = "<e1 attr1='value1'>Something1</e1><e2 attr2='value2'>Something2</e2>";
+ subtree.innerHTML = s;
+ subtree.addEventListener("DOMSubtreeModified", subtreeModified);
+
+ subtreeModifiedCount = 0;
+ subtree.firstChild.firstChild.data = "foo";
+ is(subtreeModifiedCount, 1,
+ "Editing character data should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ subtree.firstChild.removeChild(subtree.firstChild.firstChild);
+ is(subtreeModifiedCount, 1,
+ "Removing a child node should have dispatched a DOMSubtreeModified event");
+
+ subtree.innerHTML = s;
+ subtreeModifiedCount = 0;
+ subtree.firstChild.firstChild.remove();
+ is(subtreeModifiedCount, 1,
+ "Removing a child node should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ subtree.firstChild.setAttribute("foo", "bar");
+ is(subtreeModifiedCount, 1,
+ "Setting an attribute should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ subtree.textContent = "foobar";
+ is(subtreeModifiedCount, 1,
+ "Setting .textContent should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ subtree.innerHTML = s;
+ is(subtreeModifiedCount, 1,
+ "Setting .innerHTML should have dispatched a DOMSubtreeModified event");
+
+ subtreeModifiedCount = 0;
+ subtree.removeEventListener("DOMSubtreeModified", subtreeModified);
+ subtree.appendChild(document.createTextNode(""));
+ subtree.addEventListener("DOMSubtreeModified", subtreeModified);
+ subtree.normalize();
+ is(subtreeModifiedCount, 1,
+ "Calling normalize() should have dispatched a DOMSubtreeModified event");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug299673-1.html b/dom/events/test/test_bug299673-1.html
new file mode 100644
index 0000000000..c3662cc9b2
--- /dev/null
+++ b/dom/events/test/test_bug299673-1.html
@@ -0,0 +1,61 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=299673
+-->
+<head>
+ <title>Test #1 for Bug 299673</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 id="Body">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=299673">Mozilla Bug 299673</a>
+<p id="display">
+
+ <SELECT id="Select1" onchange="log(event); OpenWindow()" onfocus="log(event); " onblur="log(event)">
+ <OPTION selected>option1</OPTION>
+ <OPTION>option2</OPTION>
+ <OPTION>option3</OPTION>
+ </SELECT>
+
+ <INPUT id="Text1" type="text" onfocus="log(event)" onblur="log(event)">
+ <INPUT id="Text2" type="text" onfocus="log(event)" onblur="log(event)">
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+
+<pre id="test">
+
+<script src="bug299673.js"></script>
+
+<script class="testbody" type="text/javascript">
+
+/** Test #1 for Bug 299673 **/
+function doTest(expectedEventLog) {
+ var eventLogForNewWindow = '\
+ : Test with browser.link.open_newwindow = 2\n\
+: focus top-doc\n\
+SELECT(Select1): focus \n\
+SELECT(Select1): change \n\
+ : >>> OpenWindow\n\
+: blur top-doc\n\
+: focus popup-doc\n\
+INPUT(popupText1): focus \n\
+ : <<< OpenWindow\n\
+SELECT(Select1): blur \n\
+INPUT(popupText1): blur \n\
+: blur popup-doc\n\
+: focus top-doc\n\
+'
+
+ setPrefAndDoTest(eventLogForNewWindow,'Body',2); // 2 = open new window as window
+}
+
+todo(false, "Please write a test for bug 299673 that actually works, see bug 553417");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug299673-2.html b/dom/events/test/test_bug299673-2.html
new file mode 100644
index 0000000000..c26a08009f
--- /dev/null
+++ b/dom/events/test/test_bug299673-2.html
@@ -0,0 +1,60 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=299673
+-->
+<head>
+ <title>Test #2 for Bug 299673</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 id="Body">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=299673">Mozilla Bug 299673</a>
+<p id="display">
+
+ <SELECT id="Select1" onchange="log(event); OpenWindow()" onfocus="log(event); " onblur="log(event)">
+ <OPTION selected>option1</OPTION>
+ <OPTION>option2</OPTION>
+ <OPTION>option3</OPTION>
+ </SELECT>
+
+ <INPUT id="Text1" type="text" onfocus="log(event)" onblur="log(event)">
+ <INPUT id="Text2" type="text" onfocus="log(event)" onblur="log(event)">
+
+</p>
+<div id="content" style="display: none">
+
+</div>
+
+<pre id="test">
+
+<script src="bug299673.js"></script>
+
+<script class="testbody" type="text/javascript">
+
+/** Test #2 for Bug 299673 **/
+function doTest(expectedEventLog) {
+ var eventLogForNewTab = '\
+ : Test with browser.link.open_newwindow = 3\n\
+: focus top-doc\n\
+SELECT(Select1): focus \n\
+SELECT(Select1): change \n\
+ : >>> OpenWindow\n\
+: blur top-doc\n\
+: focus popup-doc\n\
+INPUT(popupText1): focus \n\
+ : <<< OpenWindow\n\
+SELECT(Select1): blur \n\
+INPUT(popupText1): blur \n\
+: blur popup-doc\n\
+: focus top-doc\n\
+'
+ setPrefAndDoTest(eventLogForNewTab,'Body',3); // 3 = open new window as tab
+}
+
+todo(false, "Please write a test for bug 299673 that actually works, see bug 553417");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug322588.html b/dom/events/test/test_bug322588.html
new file mode 100644
index 0000000000..25008cb375
--- /dev/null
+++ b/dom/events/test/test_bug322588.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=322588
+-->
+<head>
+ <title>Test for Bug 322588 - onBlur window close no longer works</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=322588">Mozilla Bug 322588 - onBlur window close no longer works</a>
+<p id="display">
+<a id="link" href="javascript:pop350d('bug322588-popup.html#target')">Openwindow</a><br>
+The opened window should not directly close when clicking on the Openwindow link
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 322588 **/
+
+var result = "";
+
+var w;
+function pop350d(url) {
+ w = window.open();
+ w.addEventListener("unload", function () { result += " unload";});
+ w.addEventListener("load", function () { result += " load"; setTimeout(done, 1000);});
+ w.addEventListener("blur", function () { result += " blur";});
+ w.location = url;
+}
+
+function doTest() {
+ try {
+ sendMouseEvent({type:'click'}, 'link');
+ } catch(e) {
+ if (w)
+ w.close();
+ throw e;
+ }
+}
+
+function done() {
+ is(result," unload load","unexpected events"); // The first unload is for about:blank
+ if (w)
+ w.close();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({"set": [
+ ["browser.newtab.preload", false]
+ ]}, doTest);
+});
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug328885.html b/dom/events/test/test_bug328885.html
new file mode 100644
index 0000000000..2906c7e024
--- /dev/null
+++ b/dom/events/test/test_bug328885.html
@@ -0,0 +1,135 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=328885
+-->
+<head>
+ <title>Test for Bug 328885</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=328885">Mozilla Bug 328885</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<input type="text" id="inputelement"
+ style="position: absolute; left: 0px; top: 0px;">
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 328885 **/
+
+ var inputelement = null;
+ var mutationCount = 0;
+
+ function mutationListener(evt) {
+ ++mutationCount;
+ }
+
+ function clickTest() {
+ inputelement.addEventListener("DOMSubtreeModified", mutationListener);
+ inputelement.addEventListener("DOMNodeInserted", mutationListener);
+ inputelement.addEventListener("DOMNodeRemoved", mutationListener);
+ inputelement.addEventListener("DOMNodeRemovedFromDocument", mutationListener);
+ inputelement.addEventListener("DOMNodeInsertedIntoDocument", mutationListener);
+ inputelement.addEventListener("DOMAttrModified", mutationListener);
+ inputelement.addEventListener("DOMCharacterDataModified", mutationListener);
+
+ inputelement.addEventListener('click',
+ function(event) {
+ var evt = SpecialPowers.wrap(event);
+ ok(SpecialPowers.wrap(HTMLDivElement).isInstance(evt.originalTarget),
+ "(1) Wrong originalTarget!");
+ is(SpecialPowers.unwrap(evt.originalTarget.parentNode), inputelement,
+ "(2) Wront parent node!");
+ ok(mutationCount == 0, "(3) No mutations should have happened! [" + mutationCount + "]");
+ evt.originalTarget.textContent = "foo";
+ ok(mutationCount == 0, "(4) Mutation listener shouldn't have been called! [" + mutationCount + "]");
+ evt.originalTarget.innerHTML = "foo2";
+ ok(mutationCount == 0, "(5) Mutation listener shouldn't have been called! [" + mutationCount + "]");
+ evt.originalTarget.lastChild.data = "bar";
+ ok(mutationCount == 0, "(6) Mutation listener shouldn't have been called! [" + mutationCount + "]");
+
+ var r = SpecialPowers.wrap(document.createRange());
+ r.selectNodeContents(evt.originalTarget);
+ r.deleteContents();
+ ok(mutationCount == 0, "(7) Mutation listener shouldn't have been called! [" + mutationCount + "]");
+
+ evt.originalTarget.textContent = "foo";
+ ok(mutationCount == 0, "(8) Mutation listener shouldn't have been called! [" + mutationCount + "]");
+ r = SpecialPowers.wrap(document.createRange());
+ r.selectNodeContents(evt.originalTarget);
+ r.extractContents();
+ ok(mutationCount == 0, "(9) Mutation listener shouldn't have been called! [" + mutationCount + "]");
+
+ evt.originalTarget.setAttribute("foo", "bar");
+ ok(mutationCount == 0, "(10) Mutation listener shouldn't have been called! ["+ mutationCount + "]");
+
+ // We played with the internal div, now restore its initial state
+ evt.originalTarget.replaceChildren(new Text());
+
+ // Same tests with non-native-anononymous element.
+ // mutationCount should be increased by 2 each time, since there is
+ // first a mutation specific event and then DOMSubtreeModified.
+ inputelement.textContent = "foo";
+ ok(mutationCount == 2, "(11) Mutation listener should have been called! [" + mutationCount + "]");
+ inputelement.lastChild.data = "bar";
+ ok(mutationCount == 4, "(12) Mutation listener should have been called! [" + mutationCount + "]");
+
+ r = document.createRange();
+ r.selectNodeContents(inputelement);
+ r.deleteContents();
+ ok(mutationCount == 6, "(13) Mutation listener should have been called! [" + mutationCount + "]");
+
+ inputelement.textContent = "foo";
+ ok(mutationCount == 8, "(14) Mutation listener should have been called! [" + mutationCount + "]");
+ r = document.createRange();
+ r.selectNodeContents(inputelement);
+ r.extractContents();
+ ok(mutationCount == 10, "(15) Mutation listener should have been called! [" + mutationCount + "]");
+
+ inputelement.setAttribute("foo", "bar");
+ ok(mutationCount == 12, "(16) Mutation listener should have been called! ["+ mutationCount + "]");
+
+ // Then try some mixed mutations. The mutation handler of non-native-a
+ inputelement.addEventListener("DOMAttrModified",
+ function (evt2) {
+ evt.originalTarget.setAttribute("foo", "bar" + mutationCount);
+ ok(evt.originalTarget.getAttribute("foo") == "bar" + mutationCount,
+ "(17) Couldn't update the attribute?!?");
+ });
+ inputelement.setAttribute("foo", "");
+ ok(mutationCount == 14, "(18) Mutation listener should have been called! ["+ mutationCount + "]");
+
+ inputelement.textContent = "foo";
+ ok(mutationCount == 16, "(19) Mutation listener should have been called! ["+ mutationCount + "]");
+ inputelement.addEventListener("DOMCharacterDataModified",
+ function (evt2) {
+ evt.originalTarget.textContent = "bar" + mutationCount;
+ });
+ // This one deletes and inserts a new node, then DOMSubtreeModified.
+ inputelement.textContent = "bar";
+ ok(mutationCount == 19, "(20) Mutation listener should have been called! ["+ mutationCount + "]");
+ });
+ synthesizeMouseAtCenter(inputelement, {}, window);
+ SimpleTest.finish();
+ }
+
+ function doTest() {
+ inputelement = document.getElementById('inputelement');
+ inputelement.focus();
+ setTimeout(clickTest, 100);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ addLoadEvent(doTest);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug336682.js b/dom/events/test/test_bug336682.js
new file mode 100644
index 0000000000..e673f1eb99
--- /dev/null
+++ b/dom/events/test/test_bug336682.js
@@ -0,0 +1,96 @@
+/*
+ * Helper functions for online/offline events tests.
+ *
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+var gState = 0;
+/**
+ * After all the on/offline handlers run,
+ * gState is expected to be equal to MAX_STATE.
+ */
+var MAX_STATE;
+
+function trace(text) {
+ var t = text.replace(/&/g, "&" + "amp;").replace(/</g, "&" + "lt;") + "<br>";
+ //document.getElementById("display").innerHTML += t;
+}
+
+/**
+ * Returns a handler function for an online/offline event. The returned handler
+ * ensures the passed event object has expected properties and that the handler
+ * is called at the right moment (according to the gState variable).
+ * @param nameTemplate The string identifying the hanlder. '%1' in that
+ * string will be replaced with the event name.
+ * @param eventName 'online' or 'offline'
+ * @param expectedStates an array listing the possible values of gState at the
+ * moment the handler is called. The handler increases
+ * gState by one before checking if it's listed in
+ * expectedStates.
+ */
+function makeHandler(nameTemplate, eventName, expectedStates) {
+ return function (e) {
+ var name = nameTemplate.replace(/%1/, eventName);
+ ++gState;
+ trace(name + ": gState=" + gState);
+ ok(
+ expectedStates.includes(gState),
+ "handlers called in the right order: " +
+ name +
+ " is called, " +
+ "gState=" +
+ gState +
+ ", expectedStates=" +
+ expectedStates
+ );
+ ok(e.constructor == Event, "event should be an Event");
+ ok(e.type == eventName, "event type should be " + eventName);
+ ok(!e.bubbles, "event should not bubble");
+ ok(!e.cancelable, "event should not be cancelable");
+ ok(e.target == window, "target should be the window");
+ };
+}
+
+function doTest() {
+ var iosvc = SpecialPowers.Cc["@mozilla.org/network/io-service;1"].getService(
+ SpecialPowers.Ci.nsIIOService
+ );
+ iosvc.manageOfflineStatus = false;
+ iosvc.offline = false;
+ ok(
+ navigator.onLine,
+ "navigator.onLine should be true, since we've just " +
+ "set nsIIOService.offline to false"
+ );
+
+ gState = 0;
+
+ trace("setting iosvc.offline = true");
+ iosvc.offline = true;
+ trace("done setting iosvc.offline = true");
+ ok(
+ !navigator.onLine,
+ "navigator.onLine should be false when iosvc.offline == true"
+ );
+ ok(
+ gState == window.MAX_STATE,
+ "offline event: all registered handlers should have been invoked, " +
+ "actual: " +
+ gState
+ );
+
+ gState = 0;
+ trace("setting iosvc.offline = false");
+ iosvc.offline = false;
+ trace("done setting iosvc.offline = false");
+ ok(
+ navigator.onLine,
+ "navigator.onLine should be true when iosvc.offline == false"
+ );
+ ok(
+ gState == window.MAX_STATE,
+ "online event: all registered handlers should have been invoked, " +
+ "actual: " +
+ gState
+ );
+}
diff --git a/dom/events/test/test_bug336682_1.html b/dom/events/test/test_bug336682_1.html
new file mode 100644
index 0000000000..7d3ef6b345
--- /dev/null
+++ b/dom/events/test/test_bug336682_1.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Bug 336682: online/offline events tests.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+-->
+<head>
+ <title>Test for Bug 336682 (online/offline events)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body ononline="trace('<body ononline=...>');
+ bodyOnonline(this, event)"
+ onoffline="trace('<body onoffline=...>'); bodyOnoffline(this, event)"
+ >
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=336682">Mozilla Bug 336682</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+<script type="text/javascript" src="test_bug336682.js"></script>
+
+<script class="testbody" type="text/javascript">
+
+function makeBodyHandler(eventName) {
+ return function (aThis, aEvent) {
+ var handler = makeHandler("<body on%1='...'>", eventName, [1]);
+ handler(aEvent);
+ }
+}
+addLoadEvent(function() {
+ /** @see test_bug336682.js */
+ MAX_STATE = 2;
+
+ for (var event of ["online", "offline"]) {
+ window["bodyOn" + event] = makeBodyHandler(event);
+
+ window.addEventListener(
+ event,
+ makeHandler("window.addEventListener('%1', ..., false)",
+ event, [2]));
+ }
+
+ doTest();
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug336682_2.xhtml b/dom/events/test/test_bug336682_2.xhtml
new file mode 100644
index 0000000000..b9710daf28
--- /dev/null
+++ b/dom/events/test/test_bug336682_2.xhtml
@@ -0,0 +1,58 @@
+<?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"?>
+<!--
+Bug 336682: online/offline events tests.
+
+Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/licenses/publicdomain/
+-->
+<window title="Mozilla Bug 336682"
+ onoffline="trace('lt;body onoffline=...'); windowOnoffline(this, event)"
+ ononline="trace('lt;body ononline=...'); windowOnonline(this, event)"
+
+ 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>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=336682">
+Mozilla Bug 336682 (online/offline events)</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<script type="text/javascript" src="test_bug336682.js"/>
+<script class="testbody" type="text/javascript">
+<![CDATA[
+addLoadEvent(function() {
+ /** @see test_bug336682.js */
+ MAX_STATE = 2;
+
+ function makeWindowHandler(eventName) {
+ return function (aThis, aEvent) {
+ var handler = makeHandler("<body on%1='...'>", eventName, [1]);
+ handler(aEvent);
+ }
+ }
+
+ for (var event of ["online", "offline"]) {
+ window["windowOn" + event] = makeWindowHandler(event);
+
+ window.addEventListener(
+ event,
+ makeHandler("window.addEventListener('%1', ..., false)",
+ event, [2]));
+ }
+
+ doTest();
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForExplicitFinish();
+]]>
+</script>
+
+</window>
diff --git a/dom/events/test/test_bug367781.html b/dom/events/test/test_bug367781.html
new file mode 100644
index 0000000000..30f5592498
--- /dev/null
+++ b/dom/events/test/test_bug367781.html
@@ -0,0 +1,53 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=367781
+-->
+<head>
+ <title>Test for Bug 367781</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=367781">Mozilla Bug 367781</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug **/
+var eventCounter = 0;
+
+function handler(e) {
+ if (e.type == "DOMNodeInserted") {
+ ++eventCounter;
+ }
+}
+
+function doTest() {
+ var i1 = document.getElementById('i1');
+ var i2 = document.getElementById('i2');
+ var pre = i1.contentDocument.getElementsByTagName("pre")[0];
+ pre.addEventListener("DOMNodeInserted", handler);
+ pre.textContent = pre.textContent + pre.textContent;
+ ok(eventCounter == 1, "DOMNodeInserted should have been dispatched");
+
+ pre.remove();
+ i2.contentDocument.adoptNode(pre);
+ i2.contentDocument.body.appendChild(pre);
+ ok(eventCounter == 2, "DOMNodeInserted should have been dispatched in the new document");
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+addLoadEvent(SimpleTest.finish);
+
+</script>
+</pre>
+<iframe id="i1" srcdoc="&lt;html&gt;&lt;body&gt;&lt;pre&gt;Foobar&lt;/pre&gt;&lt;/body&gt;&lt;/html&gt;"></iframe>
+<iframe id="i2" srcdoc="&lt;html&gt;&lt;body&gt;&lt;/body&gt;&lt;/html&gt;"></iframe>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug379120.html b/dom/events/test/test_bug379120.html
new file mode 100644
index 0000000000..6df6b2c639
--- /dev/null
+++ b/dom/events/test/test_bug379120.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=379120
+-->
+<head>
+ <title>Test for Bug 379120</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=379120">Mozilla Bug 379120</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 379120 **/
+
+ var originalString = "<test></test>";
+
+ // Parse the content into an XMLDocument
+ var parser = new DOMParser();
+ var originalDoc = parser.parseFromString(originalString, "text/xml");
+
+ var stylesheetText =
+ "<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' " +
+ "version='1.0' xmlns='http://www.w3.org/1999/xhtml'> " +
+
+ "<xsl:output method='xml' version='1.0' encoding='UTF-8' " +
+ "doctype-system='http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' " +
+ "doctype-public='-//W3C//DTD XHTML 1.0 Transitional//EN' /> " +
+
+ "<xsl:template match='/'>" +
+ "<div onload='var i = 1'/>" +
+ "<xsl:apply-templates />" +
+ "</xsl:template>" +
+ "</xsl:stylesheet>";
+ var stylesheet = parser.parseFromString(stylesheetText, "text/xml");
+
+ var processor = new XSLTProcessor();
+
+ var targetDocument;
+ processor.importStylesheet (stylesheet);
+ var transformedDocument = processor.transformToDocument (originalDoc);
+ is(transformedDocument.documentElement.getAttribute("onload"),
+ "var i = 1");
+ is(transformedDocument.documentElement.onload, null, "Shouldn't have onload handler");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug402089.html b/dom/events/test/test_bug402089.html
new file mode 100644
index 0000000000..905a47a5c4
--- /dev/null
+++ b/dom/events/test/test_bug402089.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=402089
+-->
+<head>
+ <title>Test for Bug 402089</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>
+<!-- setTimeout so that the test starts after paint suppression ends -->
+<body onload="setTimeout(doTest,0);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=402089">Mozilla Bug 402089</a>
+<p id="display"></p>
+<div id="content">
+ <pre id="result1"></pre>
+ <pre id="result2"></pre>
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 402089 **/
+
+var cachedEvent = null;
+
+function testCachedEvent() {
+ testEvent('result2');
+ ok((document.getElementById('result1').textContent ==
+ document.getElementById('result2').textContent),
+ "Event coordinates should be the same after dispatching.");
+ SimpleTest.finish();
+}
+
+function testEvent(res) {
+ var s = cachedEvent.type + "\n";
+ s += "clientX: " + cachedEvent.clientX + ", clientY: " + cachedEvent.clientY + "\n";
+ s += "screenX: " + cachedEvent.screenX + ", screenY: " + cachedEvent.screenY + "\n";
+ s += "layerX: " + cachedEvent.layerX + ", layerY: " + cachedEvent.layerY + "\n";
+ s += "pageX: " + cachedEvent.pageX + ", pageY: " + cachedEvent.pageY + "\n";
+ document.getElementById(res).textContent += s;
+}
+
+function clickHandler(e) {
+ cachedEvent = e;
+ testEvent('result1');
+ e.stopPropagation();
+ e.preventDefault();
+ window.removeEventListener("click", clickHandler, true);
+ setTimeout(testCachedEvent, 10);
+}
+
+function doTest() {
+ window.addEventListener("click", clickHandler, true);
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ utils.sendMouseEvent("mousedown", 1, 1, 0, 1, 0);
+ utils.sendMouseEvent("mouseup", 1, 1, 0, 1, 0);
+
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug405632.html b/dom/events/test/test_bug405632.html
new file mode 100644
index 0000000000..5eae056307
--- /dev/null
+++ b/dom/events/test/test_bug405632.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=405632
+-->
+<head>
+ <title>Test for Bug 405632</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=405632">Mozilla Bug 405632</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 405632 **/
+
+ var me = document.createEvent("mouseevent");
+ me.initMouseEvent("foo", false, false, window, 0, 100, 100, 100, 100,
+ false, false, false, false, 0, null);
+ ok(me.clientX == me.pageX,
+ "mouseEvent.clientX should be the same as mouseEvent.pageX when event is initialized manually");
+ ok(me.clientY == me.pageY,
+ "mouseEvent.clientY should be the same as mouseEvent.pageY when event is initialized manually");
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug409604.html b/dom/events/test/test_bug409604.html
new file mode 100644
index 0000000000..91dcb5defc
--- /dev/null
+++ b/dom/events/test/test_bug409604.html
@@ -0,0 +1,379 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=409604
+-->
+<head>
+ <title>Test for Bug 409604</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 id="body">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=409604">Mozilla Bug 409604</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+ /** Test for Bug 409604 **/
+
+ var expectedFocus = "a,c,d,e,f,g,h,i,j,k,l,m,n,p,x,y";
+ // XXX the "map" test is causing trouble, see bug 433089
+ var focusArray = expectedFocus.split(",");
+ var unfocusableElementId = "invalid";
+ var unfocusableTags = [
+ {tag: "abbr", content: "text", attribs: {title: "something"}},
+ {tag: "acronym", content: "text", attribs: {title: "something"}},
+ {tag: "address", content: "text"},
+ {tag: "b", content: "text"},
+ {tag: "bdo", content: "text"},
+ {tag: "big", content: "text"},
+ {tag: "blockquote", content: "text"},
+ {tag: "caption", content: "text", parent: "table", where: "first"},
+ {tag: "cite", content: "text"},
+ {tag: "code", content: "text"},
+ {tag: "dd", content: "text", parent: "dl"},
+ {tag: "del", content: "text"},
+ {tag: "dfn", content: "text", attribs: {title: "something"}},
+ {tag: "div", content: "text"},
+ {tag: "dl", content: "<dd>text</dd>", parent: "dl"},
+ {tag: "dt", content: "text", parent: "dl"},
+ {tag: "em", content: "text"},
+ {tag: "fieldset", content: "text"},
+ {tag: "form", content: "text", attribs: {action: "any.html"}},
+ {tag: "h1", content: "text"},
+ {tag: "h2", content: "text"},
+ {tag: "h3", content: "text"},
+ {tag: "h4", content: "text"},
+ {tag: "h5", content: "text"},
+ {tag: "h6", content: "text"},
+ {tag: "hr"},
+ {tag: "i", content: "text"},
+ {tag: "img", attribs: {src: "any.png", alt: "image"}},
+ {tag: "ins", content: "text"},
+ {tag: "kbd", content: "text"},
+ {tag: "li", content: "text", parent: "ol"},
+ {tag: "li", content: "text", parent: "ul"},
+ {tag: "noscript", content: "text"},
+ {tag: "ol", content: "<li>text</li>"},
+ {tag: "optgroup", content: "<option>text</option>", attribs: {label: "some label"}, parent: "select"},
+ {tag: "option", content: "text", parent: "select"},
+ {tag: "p", content: "text"},
+ {tag: "pre", content: "text"},
+ {tag: "q", content: "text"},
+ {tag: "samp", content: "text"},
+ {tag: "small", content: "text"},
+ {tag: "span", content: "text"},
+ {tag: "strong", content: "text"},
+ {tag: "sub", content: "text"},
+ {tag: "sup", content: "text"},
+ {tag: "tt", content: "text"},
+ {tag: "ul", content: "<li>text</li>"},
+ {tag: "var", content: "text"}
+ ];
+ var invalidElements = [
+ "body",
+ "col",
+ "colgroup",
+// XXX the "map" test is causing trouble, see bug 433089
+// "map",
+ "table",
+ "tbody",
+ "td",
+ "tfoot",
+ "th",
+ "thead",
+ "tr"
+ ];
+
+ function handleFocus(e) {
+ ok("accessKey" in e, "(focus) accesskey property not found on element");
+ var expected = focusArray.shift();
+ // "k" and "n" are a special cases because the element receiving the focus
+ // is not the element which has the accesskey.
+ if (expected == "k" || expected == "n") {
+ ok(e.value == "test for label", "(focus) unexpected element: " + e.value +
+ " expected: " + "test for label");
+ // "l" is a special case because the element receiving the focus is not
+ // the element which has the accesskey.
+ } else if (expected == "l") {
+ ok(e.value == "test for legend", "(focus) unexpected element: " + e.value +
+ " expected: " + "test for legend");
+ } else {
+ ok(expected == e.accessKey, "(focus) unexpected element: " + e.accessKey +
+ " expected: " + expected);
+ }
+ }
+
+ function handleClick(e) {
+ ok("accessKey" in e, "(click) accesskey property not found on element");
+ }
+
+ function handleInvalid(e) {
+ ok("accessKey" in e, "(invalid) accesskey property not found on element");
+ ok(false, "(invalid) accesskey should not have any effect on this element: " +
+ e.localName);
+ }
+
+ function pressAccessKey(key) {
+ synthesizeKey(key.key, {altKey: true, shiftKey: true});
+ }
+
+ function testFocusableElements() {
+ for (var code = "a".charCodeAt(0); code <= "y".charCodeAt(0); ++ code) {
+ // XXX the "map" test is causing trouble, see bug 433089
+ if (code == "b".charCodeAt(0))
+ continue;
+ var accessChar = String.fromCharCode(code).toUpperCase();
+ pressAccessKey({key: accessChar});
+ }
+ ok(!focusArray.length, "(focus) unhandled elements remaining: " + focusArray.join(","));
+ }
+
+ function createUnfocusableElement(elem, accesskey) {
+ ok("tag" in elem, "invalid object passed to createUnfocusableElement: " + elem.toString());
+ var e = document.createElement(elem.tag);
+ if ("content" in elem) {
+ e.innerHTML = elem.content;
+ }
+ if ("attribs" in elem) {
+ for (var attr in elem.attribs) {
+ e.setAttribute(attr, elem.attribs[attr]);
+ }
+ }
+ e.setAttribute("accesskey", accesskey);
+ e.setAttribute("onclick", "handleClick(event.target); event.preventDefault();");
+ e.setAttribute("onfocus", "handleInvalid(event.target);");
+ var parent = null;
+ var elementToInsert = null;
+ if ("parent" in elem) {
+ parent = document.getElementById(elem.parent);
+ elementToInsert = e;
+ } else {
+ parent = document.getElementById("tbody");
+ elementToInsert = document.createElement("tr");
+ var td = document.createElement("td");
+ td.textContent = elem.tag;
+ elementToInsert.appendChild(td);
+ td = document.createElement("td");
+ td.appendChild(e);
+ elementToInsert.appendChild(td);
+ }
+ ok(parent != null, "parent element not specified for element: " + elem.tag);
+ ok(elementToInsert != null, "elementToInsert not specified for element: " + elem.tag);
+ elementToInsert.setAttribute("id", unfocusableElementId);
+ if ("where" in elem) {
+ if (elem.where == "first") {
+ parent.insertBefore(elementToInsert, parent.firstChild);
+ } else {
+ ok(false, "invalid where value specified for element: " + elem.tag);
+ }
+ } else {
+ parent.appendChild(elementToInsert);
+ }
+ }
+
+ function destroyUnfocusableElement() {
+ var el = document.getElementById(unfocusableElementId);
+ ok(el != null, "unfocusable element not found");
+ el.remove();
+ ok(document.getElementById(unfocusableElementId) == null, "unfocusable element not properly removed");
+ }
+
+ function testUnfocusableElements() {
+ var i, e;
+ for (i = 0; i < unfocusableTags.length; ++ i) {
+ createUnfocusableElement(unfocusableTags[i], "z");
+ pressAccessKey({key: "Z"});
+ destroyUnfocusableElement();
+ }
+ for (i = 0; i < invalidElements.length; ++ i) {
+ e = document.getElementById(invalidElements[i]);
+ ok(e != null, "element with ID " + invalidElements[i] + " not found");
+ e.setAttribute("accesskey", "z");
+ e.setAttribute("onclick", "handleClick(event.target); event.preventDefault();");
+ e.setAttribute("onfocus", "handleInvalid(event.target);");
+ pressAccessKey({key: "Z"});
+ e.removeAttribute("accesskey");
+ e.removeAttribute("onclick");
+ e.removeAttribute("onfocus");
+ }
+ }
+
+ function start() {
+ testFocusableElements();
+ testUnfocusableElements();
+ SimpleTest.finish();
+ }
+
+ function doTest() {
+ SpecialPowers.pushPrefEnv({"set": [["ui.key.contentAccess", 5]]}, start);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+
+</script>
+</pre>
+ <table id="table">
+ <thead id="thead">
+ <tr id="tr"><th id="th">Test header</th><th></th></tr>
+ </thead>
+ <tfoot id="tfoot">
+ <tr><td id="td">Test footer</td><td></td></tr>
+ </tfoot>
+ <tbody id="tbody">
+ <colgroup id="colgroup">
+ <col id="col"></col>
+ <col></col>
+ </colgroup>
+ <tr>
+ <td>a</td><td><a href="#" onclick="handleClick(event.target); return false;" accesskey="a" onfocus="handleFocus(event.target);">test link"</a></td>
+ </tr>
+<!-- the "map" test is causing trouble, see bug 433089
+ <tr>
+ <td>area</td><td><img src="about:logo" width="300" height="236" usemap="#map">
+ <map id="map" name="map"><area shape="rect" coords="0,0,82,126" href="#"
+ onclick="handleClick(event.target); return false;" accesskey="b"></map>
+ </td>
+ </tr>
+-->
+ <tr>
+ <td>button</td><td><button onclick="handleClick(event.target);" accesskey="c" onfocus="handleFocus(event.target);">test button"</button></td>
+ </tr>
+ <tr>
+ <td>input type="text"</td><td><input type="text" value="" onclick="handleClick(event.target);" onfocus="handleFocus(event.target);" accesskey="d"></td>
+ </tr>
+ <tr>
+ <td>input type="button"</td><td><input type="button" value="type='button'" onclick="handleClick(event.target);" onfocus="handleFocus(event.target);" accesskey="e"></td>
+ </tr>
+ <tr>
+ <td>input type="checkbox"</td><td><input type="checkbox" onclick="handleClick(event.target);" onfocus="handleFocus(event.target)" accesskey="f"></td>
+ </tr>
+ <tr>
+ <td>input type="radio"</td><td><input type="radio" name="radio" onclick="handleClick(event.target);" onfocus="handleFocus(event.target);" accesskey="g"></td>
+ </tr>
+ <tr>
+ <td>input type="password"</td><td><input type="password" onclick="handleClick(event.target);" onfocus="handleFocus(event.target);" accesskey="h"></td>
+ </tr>
+ <tr>
+ <td>input type="submit"</td><td><input type="submit" value="type='submit'" onclick="handleClick(event.target); return false;"
+ onfocus="handleFocus(event.target);" accesskey="i"></td>
+ </tr>
+ <tr>
+ <td>input type="reset"</td><td><input type="submit" value="type='reset'" onclick="handleClick(event.target);"
+ onfocus="handleFocus(event.target);" accesskey="j"></td>
+ </tr>
+ <tr>
+ <td>label</td><td><label accesskey="k" onclick="handleClick(event.target);" onfocus="handleInvalid(event.target);">test label
+ <input type="text" value="test for label" onfocus="handleFocus(event.target);" onclick="handleClick(event.target);"></label></td>
+ </tr>
+ <tr>
+ <td>legend</td><td><fieldset><legend accesskey="l">test legend</legend>
+ <input type="text" value="test for legend" onfocus="handleFocus(event.target);" onclick="handleClick(event.target);" ></fieldset></td>
+ </tr>
+ <tr>
+ <td>textarea</td><td><textarea onfocus="handleFocus(event.target);" onclick="handleClick(event.target);" accesskey="m">test text</textarea></td>
+ </tr>
+ <tr>
+ <td>label (label invisible)</td><td><label for="txt1" accesskey="n" style="display:none"
+ onclick="handleClick(event.target);" onfocus="handleInvalid(event.target);">test label</label>
+ <input type="text" id="txt1" value="test for label" onclick="handleClick(event.target);" onfocus="handleFocus(event.target);"></td>
+ </tr>
+ <tr>
+ <td>label (control invisible)</td><td><label for="txt2" accesskey="o"
+ onclick="handleClick(event.target);" onfocus="handleInvalid(event.target);">test label</label>
+ <input type="text" id="txt2" value="test for label" onclick="handleClick(event.target);"
+ onfocus="handleInvalid(event.target);" style="display:none"></td>
+ </tr>
+ <tr>
+ <td>select</td>
+ <td>
+ <select onclick="handleClick(event.target);" onfocus="handleFocus(event.target)" accesskey="p"><option>option</option></select>
+ </td>
+ </tr>
+ <tr>
+ <td>object</td>
+ <td>
+ <object onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="q">an object</object>
+ </td>
+ </tr>
+ <tr>
+ <td>a without href</td>
+ <td>
+ <a onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="r">an object</object>
+ </td>
+ </tr>
+ <tr>
+ <td>disabled button</td>
+ <td>
+ <button disabled="" onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="s">disabled</button>
+ </td>
+ </tr>
+ <tr>
+ <td>disabled input</td>
+ <td>
+ <input disabled="" onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="t"></input>
+ </td>
+ </tr>
+ <tr>
+ <td>hidden input</td>
+ <td>
+ <input type="hidden" onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="u">disabled</input>
+ </td>
+ </tr>
+ <tr>
+ <td>disabled select</td>
+ <td>
+ <select disabled onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="v">
+ <option>disabled</option>
+ </select>
+ </td>
+ </tr>
+ <tr>
+ <td>disabled textarea</td>
+ <td>
+ <textarea disabled onclick="handleClick(event.target);" onfocus="handleInvalid(event.target)" accesskey="w">disabled</textarea>
+ </td>
+ </tr>
+ <tr>
+ <td>scrollable div(focusable)</td>
+ <td>
+ <div onclick="handleClick(event.target);" onfocus="handleFocus(event.target)" accesskey="x" style="height: 50px; overflow: auto;">
+ The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
+
+ dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the
+
+ lazy dog. The quick brown fox jumps over the lazy dog.
+ The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
+
+ dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the
+
+ lazy dog. The quick brown fox jumps over the lazy dog.
+ The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the lazy
+
+ dog. The quick brown fox jumps over the lazy dog. The quick brown fox jumps over the
+
+ lazy dog. The quick brown fox jumps over the lazy dog.
+ </div>
+ </td>
+ </tr>
+ <tr>
+ <td>contenteditable div(focusable)</td>
+ <td>
+ <div onclick="handleClick(event.target);" onfocus="handleFocus(event.target)" accesskey="y" contenteditable="true">
+ Test text.....
+ </div>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ <dl id="dl"></dl>
+ <ul id="ul"></ul>
+ <ol id="ol"></ol>
+ <select id="select"></select>
+</body>
+</html>
diff --git a/dom/events/test/test_bug412567.html b/dom/events/test/test_bug412567.html
new file mode 100644
index 0000000000..86f7001fc2
--- /dev/null
+++ b/dom/events/test/test_bug412567.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=412567
+-->
+<head>
+ <title>Test for Bug 412567</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="testRedispatching(event);">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=412567">Mozilla Bug 412567</a>
+<p id="display"></p>
+<div id="content" style="display: none" onload="redispatchinHandler(event)">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 412567 **/
+
+var loadEvent = null;
+
+function redispatchinHandler(evt) {
+ is(evt.type, "load", "Wrong event type!");
+ ok(!evt.isTrusted, "Event should not be trusted!");
+ SimpleTest.finish();
+}
+
+function redispatch() {
+ ok(loadEvent.isTrusted, "Event should be trusted before redispatching!");
+ document.getElementById('content').dispatchEvent(loadEvent);
+}
+
+function testRedispatching(evt) {
+ is(evt.type, "load", "Wrong event type!");
+ ok(evt.isTrusted, "Event should be trusted!");
+ loadEvent = evt;
+ setTimeout(redispatch, 0);
+}
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug415498.xhtml b/dom/events/test/test_bug415498.xhtml
new file mode 100644
index 0000000000..c8c995fe2a
--- /dev/null
+++ b/dom/events/test/test_bug415498.xhtml
@@ -0,0 +1,97 @@
+<?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=415498
+-->
+<window title="Mozilla Bug 415498"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ onload="init()">
+
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="chrome://mochikit/content/chrome-harness.js"></script>
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=415498">Mozilla Bug 415498</a>
+
+ <p id="display"></p>
+
+ <pre id="test">
+ <script class="testbody" type="application/javascript"><![CDATA[
+ const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+ );
+
+ /** Test for Bug 415498 **/
+ SimpleTest.waitForExplicitFinish();
+
+ var gTestsIterator;
+ var gConsole;
+ var gConsoleListener;
+ var gMessages = [];
+
+ function init() {
+ gTestsIterator = testsIterator();
+
+ gConsole = Cc["@mozilla.org/consoleservice;1"].
+ getService(Ci.nsIConsoleService);
+
+ gConsoleListener = {
+ observe(aObject) {
+ gMessages.push(aObject);
+ }
+ };
+ gConsole.registerListener(gConsoleListener);
+
+ nextTest();
+ }
+
+ function nextTest() {
+ let {done} = gTestsIterator.next();
+ if (done) {
+ if (gConsole && gConsoleListener) {
+ gConsole.unregisterListener(gConsoleListener);
+ }
+ SimpleTest.finish();
+ }
+ }
+
+ function* testsIterator() {
+
+ var browser = $("browser");
+ browser.addEventListener("load", function() {
+ setTimeout(nextTest, 0)
+ });
+
+ // 1) This document uses addEventListener to register a method throwing an exception
+ var chromeDir = getRootDirectory(window.location.href);
+ BrowserTestUtils.startLoadingURIString(browser, chromeDir + "bug415498-doc1.html");
+ yield undefined;
+
+ ok(verifyErrorReceived("HierarchyRequestError"),
+ "Error message not reported in event listener callback!");
+ gMessages = [];
+
+ // 2) This document sets window.onload to register a method throwing an exception
+ var chromeDir = getRootDirectory(window.location.href);
+ BrowserTestUtils.startLoadingURIString(browser, chromeDir + "bug415498-doc2.html");
+ yield undefined;
+
+ ok(verifyErrorReceived("HierarchyRequestError"),
+ "Error message not reported in window.onload!");
+ }
+
+ function verifyErrorReceived(errorString) {
+ for (var i = 0; i < gMessages.length; i++) {
+ if (gMessages[i].message.includes(errorString))
+ return true;
+ }
+ return false;
+ }
+ ]]></script>
+ </pre>
+</body>
+
+<browser id="browser" type="content" flex="1" src="about:blank"/>
+
+</window>
diff --git a/dom/events/test/test_bug418986-3.html b/dom/events/test/test_bug418986-3.html
new file mode 100644
index 0000000000..3ede005902
--- /dev/null
+++ b/dom/events/test/test_bug418986-3.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=418986
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test 3/3 for Bug 418986 - Resist fingerprinting by preventing exposure of screen and system info</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 id="body">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">Bug 418986</a>
+<p id="display"></p>
+<pre id="test"></pre>
+<script type="application/javascript" src="bug418986-3.js"></script>
+<script type="application/javascript">
+ // This test produces fake mouse events and checks that the screenX and screenY
+ // properties of the received event objects provide client window coordinates.
+ // Run the test once the window has loaded.
+ window.onload = () => test(true);
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug418986-3.xhtml b/dom/events/test/test_bug418986-3.xhtml
new file mode 100644
index 0000000000..426d888998
--- /dev/null
+++ b/dom/events/test/test_bug418986-3.xhtml
@@ -0,0 +1,27 @@
+<?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"?>
+<!--
+Bug 418986
+-->
+<window title="Mozilla Bug 418986"
+ 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>
+
+<body id="body" xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">
+Mozilla Bug 418986</a>
+</body>
+
+<script type="application/javascript" src="bug418986-3.js"></script>
+<script type="application/javascript"><![CDATA[
+ // This test produces fake mouse events and checks that the screenX and screenY
+ // properties of the received event objects provide client window coordinates.
+ // Run the test once the window has loaded.
+ test(false);
+]]></script>
+
+</window>
diff --git a/dom/events/test/test_bug422132.html b/dom/events/test/test_bug422132.html
new file mode 100644
index 0000000000..f9728c7f8b
--- /dev/null
+++ b/dom/events/test/test_bug422132.html
@@ -0,0 +1,125 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=422132
+-->
+<head>
+ <title>Test for Bug 422132</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.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=422132">Mozilla Bug 422132</a>
+<p id="display"></p>
+<div id="target" style="font-size: 0; width: 200px; height: 200px; overflow: auto;">
+ <div style="width: 1000px; height: 1000px;"></div>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 422132 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ SpecialPowers.pushPrefEnv({
+ "set":[["general.smoothScroll", false],
+ ["mousewheel.min_line_scroll_amount", 1],
+ ["mousewheel.system_scroll_override.enabled", false],
+ ["mousewheel.transaction.timeout", 100000],
+ ["test.events.async.enabled", true]]}, runTests)}, window);
+
+function runTests()
+{
+ var target = document.getElementById("target");
+
+ var scrollLeft = target.scrollLeft;
+ var scrollTop = target.scrollTop;
+
+ var tests = [
+ {
+ prepare() {
+ scrollLeft = target.scrollLeft;
+ scrollTop = target.scrollTop;
+ },
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.5,
+ deltaY: 0.5,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 0
+ },
+ }, {
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.5,
+ deltaY: 0.5,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 0
+ },
+ check() {
+ is(target.scrollLeft - scrollLeft, 1,
+ "not scrolled to right by 0.5px delta value with pending 0.5px delta");
+ is(target.scrollTop - scrollTop, 1,
+ "not scrolled to bottom by 0.5px delta value with pending 0.5px delta");
+ },
+ }, {
+ prepare() {
+ scrollLeft = target.scrollLeft;
+ scrollTop = target.scrollTop;
+ },
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5,
+ deltaY: 0.5,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 0
+ },
+ }, {
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5,
+ deltaY: 0.5,
+ lineOrPageDeltaX: 1,
+ lineOrPageDeltaY: 1
+ },
+ check() {
+ is(target.scrollLeft - scrollLeft, 1,
+ "not scrolled to right by 0.5 line delta value with pending 0.5 line delta");
+ is(target.scrollTop - scrollTop, 1,
+ "not scrolled to bottom by 0.5 line delta value with pending 0.5 line delta");
+ }
+ }
+ ];
+
+ var nextTest = function() {
+ var test = tests.shift();
+ if (test.prepare) {
+ test.prepare();
+ }
+
+ sendWheelAndPaint(target, 10, 10, test.event, function() {
+ if (test.check) {
+ test.check();
+ }
+ if (!tests.length) {
+ SimpleTest.finish();
+ return;
+ }
+
+ setTimeout(nextTest, 0);
+ });
+ }
+
+ nextTest();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug426082.html b/dom/events/test/test_bug426082.html
new file mode 100644
index 0000000000..1f68ea867f
--- /dev/null
+++ b/dom/events/test/test_bug426082.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=426082
+-->
+<head>
+ <title>Test for Bug 426082</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"/>
+</head>
+<body>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 426082 **/
+SimpleTest.waitForExplicitFinish();
+var subwindow = window.open("./bug426082.html", "bug426082", "width=800,height=1000");
+
+function finishTests() {
+ subwindow.close();
+ SimpleTest.finish();
+}
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug427537.html b/dom/events/test/test_bug427537.html
new file mode 100644
index 0000000000..f3d0641a97
--- /dev/null
+++ b/dom/events/test/test_bug427537.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=427537
+-->
+<head>
+ <title>Test for Bug 427537</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=427537">Mozilla Bug 427537</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 427537 **/
+
+var e = document.createEvent("CustomEvent");
+ok(e, "Should have custom event!");
+
+// Test initCustomEvent and also cycle collection handling by
+// passing reference to the event as 'detail' parameter.
+e.initCustomEvent("foobar", true, true, e);
+
+var didCallListener = false;
+document.addEventListener("foobar",
+ function(evt) {
+ didCallListener = true;
+ is(evt.type, "foobar", "Should get 'foobar' event!");
+ is(evt.detail, evt, ".detail should point to the event itself.");
+ ok(e.bubbles, "Event should bubble!");
+ ok(e.cancelable, "Event should be cancelable.");
+ }, true);
+
+document.dispatchEvent(e);
+ok(didCallListener, "Should have called listener!");
+
+e = document.createEvent("CustomEvent");
+e.initEvent("foo", true, true);
+is(e.detail, null, "Default detail should be null.");
+
+e = document.createEvent("CustomEvent");
+e.initCustomEvent("foobar", true, true, 1);
+is(e.detail, 1, "Detail should be 1.");
+
+e = document.createEvent("CustomEvent");
+e.initCustomEvent("foobar", true, true, "test");
+is(e.detail, "test", "Detail should be 'test'.");
+
+e = document.createEvent("CustomEvent");
+e.initCustomEvent("foobar", true, true, true);
+is(e.detail, true, "Detail should be true.");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug428988.html b/dom/events/test/test_bug428988.html
new file mode 100644
index 0000000000..5caec887a0
--- /dev/null
+++ b/dom/events/test/test_bug428988.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=428988
+-->
+<head>
+ <title>Test for Bug 428988</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=428988">Mozilla Bug 428988</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 428988 **/
+
+function listenerForClick(evt) {
+ is(Math.round(evt.mozPressure*100), 56, "Wrong .mozPressure");
+}
+
+function doTest() {
+ var target = document.getElementById("testTarget");
+ target.addEventListener("click", listenerForClick, true);
+ var me = document.createEvent("MouseEvent");
+ me.initNSMouseEvent("click", true, true, window, 0, 0, 0, 0, 0,
+ false, false, false, false, 0, null, 0.56, 0);
+ target.dispatchEvent(me);
+ target.removeEventListener("click", listenerForClick, true);
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+<span id="testTarget" style="border: 1px solid black;">testTarget</span>
+</body>
+</html>
diff --git a/dom/events/test/test_bug432698.html b/dom/events/test/test_bug432698.html
new file mode 100644
index 0000000000..8dec5c2cec
--- /dev/null
+++ b/dom/events/test/test_bug432698.html
@@ -0,0 +1,223 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=432698
+-->
+<head>
+ <title>Test for Bug 432698</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=432698">Mozilla Bug 432698</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 432698 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+var outer;
+var middle;
+var inner;
+var outside;
+var container;
+var file;
+var iframe;
+var checkRelatedTarget = false;
+var expectedRelatedEnter = null;
+var expectedRelatedLeave = null;
+var mouseentercount = 0;
+var mouseleavecount = 0;
+var mouseovercount = 0;
+var mouseoutcount = 0;
+
+function sendMouseEvent(t, elem) {
+ var r = elem.getBoundingClientRect();
+ synthesizeMouse(elem, r.width / 2, r.height / 2, {type: t});
+}
+
+var expectedMouseEnterTargets = [];
+var expectedMouseLeaveTargets = [];
+
+function runTests() {
+ outer = document.getElementById("outertest");
+ middle = document.getElementById("middletest");
+ inner = document.getElementById("innertest");
+ outside = document.getElementById("outside");
+ container = document.getElementById("container");
+ file = document.getElementById("file");
+ iframe = document.getElementById("iframe");
+
+ // Make sure ESM thinks mouse is outside the test elements.
+ sendMouseEvent("mousemove", outside);
+
+ mouseentercount = 0;
+ mouseleavecount = 0;
+ mouseovercount = 0;
+ mouseoutcount = 0;
+ checkRelatedTarget = true;
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = inner;
+ expectedMouseEnterTargets = ["outertest", "middletest", "innertest"];
+ sendMouseEvent("mousemove", inner);
+ is(mouseentercount, 3, "Unexpected mouseenter event count!");
+ is(mouseovercount, 1, "Unexpected mouseover event count!");
+ is(mouseoutcount, 0, "Unexpected mouseout event count!");
+ is(mouseleavecount, 0, "Unexpected mouseleave event count!");
+ expectedRelatedEnter = inner;
+ expectedRelatedLeave = outside;
+ expectedMouseLeaveTargets = ["innertest", "middletest", "outertest"];
+ sendMouseEvent("mousemove", outside);
+ is(mouseentercount, 3, "Unexpected mouseenter event count!");
+ is(mouseovercount, 1, "Unexpected mouseover event count!");
+ is(mouseoutcount, 1, "Unexpected mouseout event count!");
+ is(mouseleavecount, 3, "Unexpected mouseleave event count!");
+
+ // Event handling over native anonymous content.
+ var r = file.getBoundingClientRect();
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = file;
+ synthesizeMouse(file, r.width / 6, r.height / 2, {type: "mousemove"});
+ is(mouseentercount, 4, "Unexpected mouseenter event count!");
+ is(mouseovercount, 2, "Unexpected mouseover event count!");
+ is(mouseoutcount, 1, "Unexpected mouseout event count!");
+ is(mouseleavecount, 3, "Unexpected mouseleave event count!");
+
+ // Moving mouse over type="file" shouldn't cause mouseover/out/enter/leave events
+ synthesizeMouse(file, r.width - (r.width / 6), r.height / 2, {type: "mousemove"});
+ is(mouseentercount, 4, "Unexpected mouseenter event count!");
+ is(mouseovercount, 2, "Unexpected mouseover event count!");
+ is(mouseoutcount, 1, "Unexpected mouseout event count!");
+ is(mouseleavecount, 3, "Unexpected mouseleave event count!");
+
+ expectedRelatedEnter = file;
+ expectedRelatedLeave = outside;
+ sendMouseEvent("mousemove", outside);
+ is(mouseentercount, 4, "Unexpected mouseenter event count!");
+ is(mouseovercount, 2, "Unexpected mouseover event count!");
+ is(mouseoutcount, 2, "Unexpected mouseout event count!");
+ is(mouseleavecount, 4, "Unexpected mouseleave event count!");
+
+ // Initialize iframe
+ iframe.contentDocument.documentElement.style.overflow = "hidden";
+ iframe.contentDocument.body.style.margin = "0px";
+ iframe.contentDocument.body.style.width = "100%";
+ iframe.contentDocument.body.style.height = "100%";
+ iframe.contentDocument.body.innerHTML =
+ "<div style='width: 100%; height: 50%; border: 1px solid black;'></div>" +
+ "<div style='width: 100%; height: 50%; border: 1px solid black;'></div>";
+ iframe.contentDocument.body.offsetLeft; // flush
+
+ iframe.contentDocument.body.firstChild.onmouseenter = menter;
+ iframe.contentDocument.body.firstChild.onmouseleave = mleave;
+ iframe.contentDocument.body.lastChild.onmouseenter = menter;
+ iframe.contentDocument.body.lastChild.onmouseleave = mleave;
+ r = iframe.getBoundingClientRect();
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = iframe;
+ // Move mouse inside the iframe.
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height / 4, {type: "mousemove"},
+ iframe.contentWindow);
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height - (r.height / 4), {type: "mousemove"},
+ iframe.contentWindow);
+ is(mouseentercount, 7, "Unexpected mouseenter event count!");
+ expectedRelatedEnter = iframe;
+ expectedRelatedLeave = outside;
+ sendMouseEvent("mousemove", outside);
+ is(mouseleavecount, 7, "Unexpected mouseleave event count!");
+
+ checkRelatedTarget = false;
+
+ iframe.contentDocument.body.firstChild.onmouseenter = null;
+ iframe.contentDocument.body.firstChild.onmouseleave = null;
+ iframe.contentDocument.body.lastChild.onmouseenter = null;
+ iframe.contentDocument.body.lastChild.onmouseleave = null;
+
+ container.onmouseenter = null;
+ container.onmouseleave = null;
+ container.onmouseout = null;
+ container.onmouseover = null;
+
+ var children = container.getElementsByTagName('*');
+ for (var i=0;i<children.length;i++) {
+ children[i].onmouseenter = null;
+ children[i].onmouseleave = null;
+ children[i].onmouseout = null;
+ children[i].onmouseover = null;
+ }
+
+ SimpleTest.finish();
+}
+
+function menter(evt) {
+ ++mouseentercount;
+ evt.stopPropagation();
+ if (expectedMouseEnterTargets.length) {
+ var t = expectedMouseEnterTargets.shift();
+ is(evt.target.id, t, "Wrong event target!");
+ }
+ is(evt.bubbles, false, evt.type + " should not bubble!");
+ is(evt.cancelable, false, evt.type + " is not cancelable!");
+ is(evt.target, evt.currentTarget, "Wrong event target!");
+ ok(!evt.relatedTarget || evt.target.ownerDocument == evt.relatedTarget.ownerDocument,
+ "Leaking nodes to another document?");
+ if (checkRelatedTarget && evt.target.ownerDocument == document) {
+ is(evt.relatedTarget, expectedRelatedEnter, "Wrong related target (mouseenter)");
+ }
+}
+
+function mleave(evt) {
+ ++mouseleavecount;
+ evt.stopPropagation();
+ if (expectedMouseLeaveTargets.length) {
+ var t = expectedMouseLeaveTargets.shift();
+ is(evt.target.id, t, "Wrong event target!");
+ }
+ is(evt.bubbles, false, evt.type + " should not bubble!");
+ is(evt.cancelable, false, evt.type + " is not cancelable!");
+ is(evt.target, evt.currentTarget, "Wrong event target!");
+ ok(!evt.relatedTarget || evt.target.ownerDocument == evt.relatedTarget.ownerDocument,
+ "Leaking nodes to another document?");
+ if (checkRelatedTarget && evt.target.ownerDocument == document) {
+ is(evt.relatedTarget, expectedRelatedLeave, "Wrong related target (mouseleave)");
+ }
+}
+
+function mover(evt) {
+ ++mouseovercount;
+ evt.stopPropagation();
+}
+
+function mout(evt) {
+ ++mouseoutcount;
+ evt.stopPropagation();
+}
+
+</script>
+</pre>
+<div id="container" onmouseenter="menter(event)" onmouseleave="mleave(event)"
+ onmouseout="mout(event)" onmouseover="mover(event)">
+ <div id="outside" onmouseout="event.stopPropagation()" onmouseover="event.stopPropagation()">foo</div>
+ <div id="outertest" onmouseenter="menter(event)" onmouseleave="mleave(event)"
+ onmouseout="mout(event)" onmouseover="mover(event)">
+ <div id="middletest" onmouseenter="menter(event)" onmouseleave="mleave(event)"
+ onmouseout="mout(event)" onmouseover="mover(event)">
+ <div id="innertest" onmouseenter="menter(event)" onmouseleave="mleave(event)"
+ onmouseout="mout(event)" onmouseover="mover(event)">foo</div>
+ </div>
+ </div>
+ <input type="file" id="file"
+ onmouseenter="menter(event)" onmouseleave="mleave(event)"
+ onmouseout="mout(event)" onmouseover="mover(event)">
+ <br>
+ <iframe id="iframe" width="50px" height="50px"
+ onmouseenter="menter(event)" onmouseleave="mleave(event)"
+ onmouseout="mout(event)" onmouseover="mover(event)"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug443985.html b/dom/events/test/test_bug443985.html
new file mode 100644
index 0000000000..01180be00e
--- /dev/null
+++ b/dom/events/test/test_bug443985.html
@@ -0,0 +1,76 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=443985
+-->
+<head>
+ <title>Test for Bug 443985</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=443985">Mozilla Bug 443985</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 443985 **/
+
+
+function listenerForNoScroll(evt) {
+ is(evt.clientX, evt.pageX, "Wrong .pageX");
+ is(evt.clientY, evt.pageY, "Wrong .pageY");
+ is(evt.screenX, 0, "Wrong .screenX");
+ is(evt.screenY, 0, "Wrong .screenY");
+ is(evt.clientX, 10, "Wrong .clientX");
+ is(evt.clientY, 10, "Wrong .clientY");
+}
+
+function listenerForScroll(evt) {
+ isnot(evt.clientX, evt.pageX, "Wrong .pageX");
+ isnot(evt.clientY, evt.pageY, "Wrong .pageY");
+ ok(evt.pageX > 3000, "Wrong .pageX");
+ ok(evt.pageY > 3000, "Wrong .pageY");
+ is(evt.screenX, 0, "Wrong .screenX");
+ is(evt.screenY, 0, "Wrong .screenY");
+ is(evt.clientX, 10, "Wrong .clientX");
+ is(evt.clientY, 10, "Wrong .clientY");
+}
+
+function doTest() {
+ window.scrollTo(0, 0);
+ var target = document.getElementById("testTarget");
+ target.addEventListener("click", listenerForNoScroll, true);
+ var me = document.createEvent("MouseEvent");
+ me.initMouseEvent("click", true, true, window, 0, 0, 0, 10, 10,
+ false, false, false, false, 0, null);
+ target.dispatchEvent(me);
+ target.removeEventListener("click", listenerForNoScroll, true);
+
+ target.scrollIntoView(true);
+ target.addEventListener("click", listenerForScroll, true);
+ me = document.createEvent("MouseEvent");
+ me.initMouseEvent("click", true, true, window, 0, 0, 0, 10, 10,
+ false, false, false, false, 0, null);
+ target.dispatchEvent(me);
+ target.addEventListener("click", listenerForNoScroll, true);
+
+ document.getElementsByTagName("a")[0].scrollIntoView(true);
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+<div style="min-height: 4000px; min-width: 4000px;"></div>
+<div style="min-width: 4000px; text-align: right;">
+ <span id="testTarget" style="border: 1px solid black;">testTarget</span>
+</div>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug447736.html b/dom/events/test/test_bug447736.html
new file mode 100644
index 0000000000..48097a0245
--- /dev/null
+++ b/dom/events/test/test_bug447736.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=447736
+-->
+<head>
+ <title>Test for Bug 447736</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=447736">Mozilla Bug 447736</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<div id="secondTarget"></div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 447736 **/
+
+var loadEvent = null;
+window.addEventListener("load",
+ function (evt) {
+ is(evt.target, window.document, "Wrong target!");
+ is(evt.originalTarget, window.document, "Wrong originalTarget!");
+ ok(evt.isTrusted, "Event should be trusted!");
+ loadEvent = evt;
+ // eslint-disable-next-line no-implied-eval
+ setTimeout("st.dispatchEvent(loadEvent)", 0);
+ }, true);
+
+var st = document.getElementById("secondTarget");
+st.addEventListener("load",
+ function (evt) {
+ is(evt.target, st, "Wrong target! (2)");
+ is(evt.originalTarget, st, "Wrong originalTarget! (2)");
+ ok(!evt.isTrusted, "Event shouldn't be trusted anymore!");
+ SimpleTest.finish();
+ }, true);
+
+SimpleTest.waitForExplicitFinish();
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug448602.html b/dom/events/test/test_bug448602.html
new file mode 100644
index 0000000000..18b4cb2d2f
--- /dev/null
+++ b/dom/events/test/test_bug448602.html
@@ -0,0 +1,303 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=448602
+-->
+<head>
+ <title>Test for Bug 448602</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=448602">Mozilla Bug 448602</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 448602 **/
+
+var els, root, l2, l3;
+
+var handlerCalled = false;
+var capturingListenerCalled = false;
+var bubblingListenerCalled = false;
+
+function clearListenerStates() {
+ handlerCalled = false;
+ capturingListenerCalled = false;
+ bubblingListenerCalled = false;
+}
+
+function runTests() {
+ els = SpecialPowers.Cc["@mozilla.org/eventlistenerservice;1"]
+ .getService(SpecialPowers.Ci.nsIEventListenerService);
+
+ // Event listener info tests
+ root = document.getElementById("testroot");
+ var infos = els.getListenerInfoFor(root);
+ is(infos.length, 0, "Element shouldn't have listeners (1)");
+
+ var listenerSource = 'handlerCalled = true;';
+ root.setAttribute("onclick", listenerSource);
+ infos = els.getListenerInfoFor(root);
+ is(infos.length, 1, "Element should have listeners (1)");
+ is(infos[0].toSource(), 'function onclick(event) {\n' + listenerSource + '\n}',
+ "Unexpected serialization (1)");
+ is(infos[0].type, "click", "Wrong type (1)");
+ is(infos[0].capturing, false, "Wrong phase (1)");
+ is(infos[0].allowsUntrusted, true, "Should allow untrusted events (1)");
+ is(SpecialPowers.unwrap(infos[0].listenerObject), root.onclick,
+ "Should have the right listener object (1)");
+
+ // Test disabling and enabling the listener.
+ ok(!handlerCalled);
+ root.click();
+ ok(handlerCalled);
+
+ clearListenerStates()
+ infos[0].enabled = false;
+ root.click();
+ ok(!handlerCalled);
+
+ clearListenerStates()
+ infos[0].enabled = true;
+ root.click();
+ ok(handlerCalled);
+ clearListenerStates();
+
+ function capturingListener() {
+ capturingListenerCalled = true;
+ }
+ function bubblingListener() {
+ bubblingListenerCalled = true;
+ }
+ root.addEventListener("click", capturingListener, true);
+ root.addEventListener("click", bubblingListener);
+ root.addEventListener("fooevent", capturingListener, true);
+ root.addEventListener("fooevent", bubblingListener);
+
+ // We now have both "click" and "fooevent" listeners.
+ // Get the new set of listener infos, because we'll want to flip certain
+ // "click" event listeners on and off in the tests below.
+ // The order of event types is not guaranteed by getListenerInfoFor; but the
+ // order of listeners for a single event is guaranteed. So we filter the infos
+ // by event type.
+ const combinedListenerInfos = [...els.getListenerInfoFor(root)];
+ const clickInfos = combinedListenerInfos.filter((info) => info.type == "click");
+
+ // Use a child node to dispatch events so that both capturing and bubbling
+ // listeners get called.
+ l2 = document.getElementById("testlevel2");
+ l2.click();
+ ok(handlerCalled);
+ ok(capturingListenerCalled);
+ ok(bubblingListenerCalled);
+ clearListenerStates();
+
+ clickInfos[0].enabled = false;
+ l2.click();
+ ok(!handlerCalled);
+ ok(capturingListenerCalled);
+ ok(bubblingListenerCalled);
+ clearListenerStates();
+ clickInfos[0].enabled = true;
+
+ clickInfos[1].enabled = false;
+ l2.click();
+ ok(handlerCalled);
+ ok(!capturingListenerCalled);
+ ok(bubblingListenerCalled);
+ clearListenerStates();
+ clickInfos[1].enabled = true;
+
+ clickInfos[2].enabled = false;
+ l2.click();
+ ok(handlerCalled);
+ ok(capturingListenerCalled);
+ ok(!bubblingListenerCalled);
+ clearListenerStates();
+ clickInfos[2].enabled = true;
+
+ root.removeEventListener("click", capturingListener, true);
+ root.removeEventListener("click", bubblingListener);
+ root.removeEventListener("fooevent", capturingListener, true);
+ root.removeEventListener("fooevent", bubblingListener);
+ root.removeAttribute("onclick");
+
+ root.setAttribute("onclick", "...invalid script...");
+ SimpleTest.expectUncaughtException(true);
+ infos = els.getListenerInfoFor(root);
+ SimpleTest.expectUncaughtException(false);
+ is(infos.length, 1);
+ is(infos[0].listenerObject, null);
+
+ root.removeAttribute("onclick");
+ infos = els.getListenerInfoFor(root);
+ is(infos.length, 0, "Element shouldn't have listeners (2)");
+
+ var l = function (e) { alert(e); };
+ root.addEventListener("foo", l, true, true);
+ root.addEventListener("foo", l, false, false);
+ infos = els.getListenerInfoFor(root);
+ is(infos.length, 2, "Element should have listeners (2)");
+ is(infos[0].toSource(), "(function (e) { alert(e); })",
+ "Unexpected serialization (2)");
+ is(infos[0].type, "foo", "Wrong type (2)");
+ is(infos[0].capturing, true, "Wrong phase (2)");
+ is(infos[0].allowsUntrusted, true, "Should allow untrusted events (2)");
+ is(SpecialPowers.unwrap(infos[0].listenerObject), l,
+ "Should have the right listener object (2)");
+ is(infos[1].toSource(), "(function (e) { alert(e); })",
+ "Unexpected serialization (3)");
+ is(infos[1].type, "foo", "Wrong type (3)");
+ is(infos[1].capturing, false, "Wrong phase (3)");
+ is(infos[1].allowsUntrusted, false, "Shouldn't allow untrusted events (1)");
+ is(SpecialPowers.unwrap(infos[1].listenerObject), l,
+ "Should have the right listener object (3)");
+
+ root.removeEventListener("foo", l, true);
+ root.removeEventListener("foo", l);
+ infos = els.getListenerInfoFor(root);
+ is(infos.length, 0, "Element shouldn't have listeners (3)");
+
+ root.onclick = l;
+ infos = els.getListenerInfoFor(root);
+ is(infos.length, 1, "Element should have listeners (3)");
+ is(infos[0].toSource(), '(function (e) { alert(e); })',
+ "Unexpected serialization (4)");
+ is(infos[0].type, "click", "Wrong type (4)");
+ is(infos[0].capturing, false, "Wrong phase (4)");
+ is(infos[0].allowsUntrusted, true, "Should allow untrusted events (3)");
+ is(SpecialPowers.unwrap(infos[0].listenerObject), l,
+ "Should have the right listener object (4)");
+
+ // Event target chain tests
+ l3 = document.getElementById("testlevel3");
+ var textnode = l3.firstChild;
+ var chain = els.getEventTargetChainFor(textnode, true);
+ ok(chain.length > 3, "Too short event target chain.");
+ ok(SpecialPowers.compare(chain[0], textnode), "Wrong chain item (1)");
+ ok(SpecialPowers.compare(chain[1], l3), "Wrong chain item (2)");
+ ok(SpecialPowers.compare(chain[2], l2), "Wrong chain item (3)");
+ ok(SpecialPowers.compare(chain[3], root), "Wrong chain item (4)");
+
+ var hasDocumentInChain = false;
+ var hasWindowInChain = false;
+ for (var i = 0; i < chain.length; ++i) {
+ if (SpecialPowers.compare(chain[i], document)) {
+ hasDocumentInChain = true;
+ } else if (SpecialPowers.compare(chain[i], window)) {
+ hasWindowInChain = true;
+ }
+ }
+
+ ok(hasDocumentInChain, "Should have document in event target chain!");
+ ok(hasWindowInChain, "Should have window in event target chain!");
+
+ try {
+ els.getListenerInfoFor(null);
+ ok(false, "Should have thrown an exception.");
+ } catch (ex) {
+ ok(true, "We should be still running.");
+ }
+ setTimeout(testAllListener, 0);
+}
+
+function dispatchTrusted(t, o) {
+ SpecialPowers.dispatchEvent(window, t, new Event("testevent", o));
+}
+
+function testAllListener() {
+ els = SpecialPowers.wrap(els);
+ var results = [];
+ var expectedResults =
+ [ { target: "testlevel3", phase: 3, trusted: false },
+ { target: "testlevel3", phase: 3, trusted: false },
+ { target: "testlevel3", phase: 3, trusted: true },
+ { target: "testlevel3", phase: 3, trusted: true },
+ { target: "testlevel3", phase: 3, trusted: true }
+ ];
+
+ function allListener(e) {
+ results.push({
+ target: e.target.id,
+ phase: e.eventPhase,
+ trusted: e.isTrusted
+ });
+ e.stopPropagation();
+ }
+ function allListenerTrustedOnly(e) {
+ results.push({
+ target: e.target.id,
+ phase: e.eventPhase,
+ trusted: e.isTrusted
+ });
+ e.stopPropagation();
+ }
+
+ els.addListenerForAllEvents(root, allListener, false, true);
+ var infos = els.getListenerInfoFor(root);
+ var nullTypes = 0;
+ for (var i = 0; i < infos.length; ++i) {
+ if (infos[i].type == null) {
+ ++nullTypes;
+ }
+ }
+ is(nullTypes, 1, "Should have one all-event-listener!");
+
+ els.addListenerForAllEvents(root, allListener, false, true, true);
+ els.addListenerForAllEvents(root, allListenerTrustedOnly, false, false, true);
+ l3.dispatchEvent(new Event("testevent", { bubbles: true, composed: true }));
+ dispatchTrusted(l3, { bubbles: true, composed: true });
+ els.removeListenerForAllEvents(root, allListener, false);
+ els.removeListenerForAllEvents(root, allListener, false, true);
+ els.removeListenerForAllEvents(root, allListenerTrustedOnly, false, true);
+ // make sure removeListenerForAllEvents works.
+ l3.dispatchEvent(new Event("testevent", { bubbles: true, composed : true }));
+ dispatchTrusted(l3, { bubbles: true, composed: true });
+
+ // Test the order of event listeners.
+ var clickListenerCalled = false;
+ var allListenerCalled = false;
+ function clickListener() {
+ clickListenerCalled = true;
+ ok(allListenerCalled, "Should have called '*' listener before normal listener!");
+ }
+ function allListener2() {
+ allListenerCalled = true;
+ ok(!clickListenerCalled, "Shouldn't have called click listener before '*' listener!");
+ }
+ root.onclick = null; // Remove the listener added in earlier tests.
+ root.addEventListener("click", clickListener);
+ els.addListenerForAllEvents(root, allListener2, false, true);
+ l3.dispatchEvent(new MouseEvent("click", { bubbles: true }));
+ root.removeEventListener("click", clickListener);
+ els.removeListenerForAllEvents(root, allListener2, false);
+ ok(allListenerCalled, "Should have called '*' listener");
+ ok(clickListenerCalled, "Should have called click listener");
+
+ is(results.length, expectedResults.length, "count");
+ for (var i = 0; i < expectedResults.length; ++i) {
+ for (var p in expectedResults[i]) {
+ is(results[i][p], expectedResults[i][p], p);
+ }
+ }
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+</script>
+</pre>
+<div id="testroot">
+ <div id="testlevel2">
+ <div id="testlevel3">
+ Test
+ </div>
+ </div>
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug450876.html b/dom/events/test/test_bug450876.html
new file mode 100644
index 0000000000..1f522a9e79
--- /dev/null
+++ b/dom/events/test/test_bug450876.html
@@ -0,0 +1,47 @@
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=450876
+-->
+<head>
+ <title>Test for Bug 450876 - Crash [@ nsEventStateManager::GetNextTabbableMapArea] with img usemap and tabindex</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=450876">Mozilla Bug 450876</a>
+<p id="display"><a href="#" id="a">link to focus from</a><img usemap="#a" tabindex="1"></p>
+<div id="content" style="display: none">
+
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 450876 **/
+
+function setTabFocus() {
+ // Override tab focus behavior on Mac */
+ SpecialPowers.pushPrefEnv({ set: [[ "accessibility.tabfocus", 7 ]] }, doTest);
+}
+
+function doTest() {
+ is(document.activeElement, document.body, "body element should be focused");
+ document.getElementById('a').focus();
+ is(document.activeElement, document.getElementById('a'), "link should have focus");
+ is(document.hasFocus(), true, "document should be focused");
+ synthesizeKey("KEY_Tab");
+ is(document.activeElement, document.body, "body element should be focused");
+ is(document.hasFocus(), false, "document should not be focused");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(setTabFocus);
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug456273.html b/dom/events/test/test_bug456273.html
new file mode 100644
index 0000000000..a4fdda958a
--- /dev/null
+++ b/dom/events/test/test_bug456273.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=456273
+-->
+<head>
+ <title>Test for Bug 456273</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=456273">Mozilla Bug 456273</a>
+<p id="display">PASS if Firefox does not crash.</p>
+<div id="content" style="display: none">
+
+</div>
+
+<div id="edit456273" contenteditable="true">text</div>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 456273 **/
+
+function doTest() {
+ var ev = new KeyboardEvent("keypress", {
+ bubbles: true,
+ cancelable: true,
+ view: null,
+ ctrlKey: true,
+ keyCode: 0,
+ charCode: "z".charCodeAt(0),
+ });
+ SpecialPowers.dispatchEvent(window, document.getElementById('edit456273'), ev);
+
+ ok(true, "PASS");
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug457672.html b/dom/events/test/test_bug457672.html
new file mode 100644
index 0000000000..7be6b79eb2
--- /dev/null
+++ b/dom/events/test/test_bug457672.html
@@ -0,0 +1,55 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=457672
+-->
+<head>
+ <title>Test for Bug 457672</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=457672">Mozilla Bug 457672</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 457672 **/
+
+var windowBlurCount = 0;
+
+function listener(evt) {
+ if (evt.type == "focus") {
+ is(windowBlurCount, 1,
+ "Window should have got blur event when opening a new tab!");
+ document.getElementsByTagName("a")[0].focus();
+ SimpleTest.finish();
+ } else if (evt.type == "blur") {
+ ++windowBlurCount;
+ }
+ document.getElementById('log').textContent += evt.target + ":" + evt.type + "\n";
+}
+
+function startTest() {
+ SpecialPowers.pushPrefEnv({"set": [["browser.link.open_newwindow", 3]]}, function() {
+ document.getElementsByTagName("a")[0].focus();
+ // Note, focus/blur don't bubble
+ window.addEventListener("focus", listener);
+ window.addEventListener("blur", listener);
+ var subwin = window.open("about:blank", "", "");
+ subwin.addEventListener("focus", function(e) { subwin.close(); });
+ });
+}
+
+addLoadEvent(startTest);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<pre id="log">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug489671.html b/dom/events/test/test_bug489671.html
new file mode 100644
index 0000000000..d9e2179f06
--- /dev/null
+++ b/dom/events/test/test_bug489671.html
@@ -0,0 +1,56 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=489671
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 489671</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=489671"
+ >Mozilla Bug 489671</a>
+<p id="display" onclick="queueNextTest(); throw 'Got click 1';"></p>
+<script>
+// override window.onerror so it won't see our exceptions
+window.onerror = function() {}
+
+var testNum = 0;
+function doTest() {
+ switch(testNum++) {
+ case 0:
+ var event = document.createEvent("MouseEvents");
+ event.initMouseEvent("click", true, true, document.defaultView,
+ 0, 0, 0, 0, 0, false, false, false, false, 0, null);
+ $("display").dispatchEvent(event);
+ break;
+ case 1:
+ var script = document.createElement("script");
+ script.textContent = "queueNextTest(); throw 'Got click 2'";
+ document.body.appendChild(script);
+ break;
+ case 2:
+ // eslint-disable-next-line no-implied-eval
+ window.setTimeout("queueNextTest(); throw 'Got click 3'", 0);
+ break;
+ case 3:
+ SimpleTest.endMonitorConsole();
+ break;
+ }
+}
+function queueNextTest() { SimpleTest.executeSoon(doTest); }
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.monitorConsole(SimpleTest.finish, [
+ { errorMessage: "uncaught exception: Got click 1" },
+ { errorMessage: "uncaught exception: Got click 2" },
+ { errorMessage: "uncaught exception: Got click 3" }
+]);
+
+doTest();
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_bug493251.html b/dom/events/test/test_bug493251.html
new file mode 100644
index 0000000000..e7c0be2d3a
--- /dev/null
+++ b/dom/events/test/test_bug493251.html
@@ -0,0 +1,181 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=493251
+-->
+<head>
+ <title>Test for Bug 493251</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=493251">Mozilla Bug 493251</a>
+<p id="display">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 493251 **/
+
+ var win;
+
+ var mouseDown = 0;
+ var mouseUp = 0;
+ var mouseClick = 0;
+
+ var keyDown = 0;
+ var keyPress = 0;
+ var keyUp = 0;
+
+ function suppressEventHandling(aSuppress) {
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+ ok(true, "suppressEventHandling: aSuppress=" + aSuppress);
+ utils.suppressEventHandling(aSuppress);
+ }
+
+ function dispatchMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers) {
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+ ok(true, "Dipatching mouse event: aType=" + aType + ", aX=" + aX + ", aY" +
+ aY + ", aButton=" + aButton + ", aClickCount=" + aClickCount +
+ ", aModifiers=" + aModifiers);
+ utils.sendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers);
+ }
+
+ function dumpEvent(aEvent) {
+ var detail = "target=" + aEvent.target + ", originalTarget=" +
+ aEvent.originalTarget + ", defaultPrevented=" +
+ aEvent.defaultPrevented + ", isTrusted=" + aEvent.isTrusted;
+ switch (aEvent.type) {
+ case "keydown":
+ case "keypress":
+ case "keyup":
+ detail += ", charCode=0x" + aEvent.charCode.toString(16) +
+ ", keyCode=0x" + aEvent.keyCode.toString(16) +
+ ", altKey=" + (aEvent.altKey ? "PRESSED" : "no") +
+ ", ctrlKey=" + (aEvent.ctrlKey ? "PRESSED" : "no") +
+ ", shiftKey=" + (aEvent.shiftKey ? "PRESSED" : "no") +
+ ", metaKey=" + (aEvent.metaKey ? "PRESSED" : "no");
+ break;
+ case "mousedown":
+ case "mouseup":
+ case "click":
+ detail += ", screenX=" + aEvent.screenX + ", screenY=" + aEvent.screenY +
+ ", clientX=" + aEvent.clientX + ", clientY=" + aEvent.clientY +
+ ", altKey=" + (aEvent.altKey ? "PRESSED" : "no") +
+ ", ctrlKey=" + (aEvent.ctrlKey ? "PRESSED" : "no") +
+ ", shiftKey=" + (aEvent.shiftKey ? "PRESSED" : "no") +
+ ", metaKey=" + (aEvent.metaKey ? "PRESSED" : "no") +
+ ", button=" + aEvent.button +
+ ", relatedTarget=" + aEvent.relatedTarget;
+ break;
+ }
+ ok(true, aEvent.type + " event is handled: " + detail);
+
+ var fm = SpecialPowers.Cc["@mozilla.org/focus-manager;1"].
+ getService(SpecialPowers.Ci.nsIFocusManager);
+ ok(true, "focused element is \"" + fm.focusedElement +
+ "\" and focused window is \"" + fm.focusedWindow +
+ "\" (the testing window is \"" + win + "\"");
+ }
+
+ function doTest() {
+ win.document.getElementsByTagName("input")[0].focus();
+ win.addEventListener("keydown",
+ function(e) { dumpEvent(e); ++keyDown; }, true);
+ win.addEventListener("keypress",
+ function(e) { dumpEvent(e); ++keyPress; }, true);
+ win.addEventListener("keyup",
+ function(e) { dumpEvent(e); ++keyUp; }, true);
+ win.addEventListener("mousedown",
+ function(e) { dumpEvent(e); ++mouseDown; }, true);
+ win.addEventListener("mouseup",
+ function(e) { dumpEvent(e); ++mouseUp; }, true);
+ win.addEventListener("click",
+ function(e) { dumpEvent(e); ++mouseClick; }, true);
+
+ ok(true, "doTest #1...");
+ synthesizeKey("a", {}, win);
+ is(keyDown, 1, "Wrong number events (1)");
+ is(keyPress, 1, "Wrong number events (2)");
+ is(keyUp, 1, "Wrong number events (3)");
+
+ ok(true, "doTest #2...");
+ suppressEventHandling(true);
+ synthesizeKey("a", {}, win);
+ is(keyDown, 1, "Wrong number events (4)");
+ is(keyPress, 1, "Wrong number events (5)");
+ is(keyUp, 1, "Wrong number events (6)");
+ suppressEventHandling(false);
+ is(keyDown, 1, "Wrong number events (7)");
+ is(keyPress, 1, "Wrong number events (8)");
+ is(keyUp, 1, "Wrong number events (9)");
+
+ setTimeout(continueTest1, 0);
+ }
+
+ function continueTest1() {
+ ok(true, "continueTest1...");
+ win.addEventListener("keydown", () => { suppressEventHandling(true); }, {once: true});
+ synthesizeKey("a", {}, win);
+ is(keyDown, 2, "Wrong number events (10)");
+ is(keyPress, 1, "Wrong number events (11)");
+ is(keyUp, 1, "Wrong number events (12)");
+ suppressEventHandling(false);
+ setTimeout(continueTest2, 0);
+ }
+
+ function continueTest2() {
+ ok(true, "continueTest2 #1...");
+ is(keyDown, 2, "Wrong number events (13)");
+ is(keyPress, 2, "Wrong number events (14)");
+ is(keyUp, 2, "Wrong number events (15)");
+
+ dispatchMouseEvent("mousedown", 5, 5, 0, 1, 0);
+ dispatchMouseEvent("mouseup", 5, 5, 0, 1, 0);
+ is(mouseDown, 1, "Wrong number events (16)");
+ is(mouseUp, 1, "Wrong number events (17)");
+ is(mouseClick, 1, "Wrong number events (18)");
+
+ ok(true, "continueTest2 #2...");
+ suppressEventHandling(true);
+ dispatchMouseEvent("mousedown", 5, 5, 0, 1, 0);
+ dispatchMouseEvent("mouseup", 5, 5, 0, 1, 0);
+ suppressEventHandling(false);
+ is(mouseDown, 1, "Wrong number events (19)");
+ is(mouseUp, 1, "Wrong number events (20)");
+ is(mouseClick, 1, "Wrong number events (21)");
+
+ setTimeout(continueTest3, 0);
+ }
+
+ function continueTest3() {
+ ok(true, "continueTest3...");
+ dispatchMouseEvent("mousedown", 5, 5, 0, 1, 0);
+ suppressEventHandling(true);
+ dispatchMouseEvent("mouseup", 5, 5, 0, 1, 0);
+ suppressEventHandling(false);
+ setTimeout(continueTest4, 1000);
+ }
+
+ function continueTest4() {
+ ok(true, "continueTest4...");
+ is(mouseDown, 2, "Wrong number events (19)");
+ is(mouseUp, 2, "Wrong number events (20)");
+ is(mouseClick, 2, "Wrong number events (21)");
+ win.close();
+ SimpleTest.finish();
+ }
+
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ win = window.open("window_bug493251.html", "_blank" , "width=500,height=500");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug508479.html b/dom/events/test/test_bug508479.html
new file mode 100644
index 0000000000..4967dd1ce8
--- /dev/null
+++ b/dom/events/test/test_bug508479.html
@@ -0,0 +1,110 @@
+<html>
+<head>
+ <title>Tests for the dragstart event</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+
+<script>
+
+var gGotHandlingDrop = false;
+var gGotNotHandlingDrop = false;
+
+SimpleTest.waitForExplicitFinish();
+
+function fireEvent(target, event) {
+ SpecialPowers.DOMWindowUtils.dispatchDOMEventViaPresShellForTesting(target, event);
+}
+
+async function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
+ var ds = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
+ getService(SpecialPowers.Ci.nsIDragService);
+
+ var dataTransfer;
+ var trapDrag = function(event) {
+ dataTransfer = event.dataTransfer;
+ dataTransfer.setData("text/plain", "Hello");;
+ dataTransfer.dropEffect = "move";
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ // need to use real mouse action
+ window.addEventListener("dragstart", trapDrag, true);
+ await synthesizePlainDragAndDrop({
+ srcElement: element,
+ stepX: 9,
+ stepY: 9,
+ expectCancelDragStart: true,
+ });
+ window.removeEventListener("dragstart", trapDrag, true);
+
+ ds.startDragSessionForTests(
+ SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_MOVE |
+ SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_COPY |
+ SpecialPowers.Ci.nsIDragService.DRAGDROP_ACTION_LINK
+ ); // Session for emulating dnd coming from another app.
+ try {
+ var event = document.createEvent("DragEvent");
+ event.initDragEvent("dragover", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
+ fireEvent(element, event);
+
+ is(ds.getCurrentSession().canDrop, shouldAllowDrop, "Unexpected .canDrop");
+ is(ds.getCurrentSession().onlyChromeDrop, shouldAllowOnlyChromeDrop,
+ "Unexpected .onlyChromeDrop");
+
+ event = document.createEvent("DragEvent");
+ event.initDragEvent("drop", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null, dataTransfer);
+ fireEvent(element, event);
+ } finally {
+ ds.endDragSession(false);
+ ok(!ds.getCurrentSession(), "There shouldn't be a drag session anymore!");
+ }
+}
+
+var chromeGotEvent = false;
+function chromeListener(e) {
+ chromeGotEvent = true;
+}
+
+async function runTests()
+{
+ var targetHandling = document.getElementById("handling_target");
+ await fireDrop(targetHandling, true, false);
+
+ is(gGotHandlingDrop, true, "Got drop on accepting element (1)");
+ is(gGotNotHandlingDrop, false, "Didn't get drop on unaccepting element (1)");
+
+ // reset
+ gGotHandlingDrop = false;
+ gGotNotHandlingDrop = false;
+
+ SpecialPowers.addChromeEventListener("drop", chromeListener, true, false);
+ var targetNotHandling = document.getElementById("nothandling_target");
+ await fireDrop(targetNotHandling, true, true);
+ SpecialPowers.removeChromeEventListener("drop", chromeListener, true);
+ ok(chromeGotEvent, "Chrome should have got drop event!");
+ is(gGotHandlingDrop, false, "Didn't get drop on accepting element (2)");
+ is(gGotNotHandlingDrop, false, "Didn't get drop on unaccepting element (2)");
+
+ SimpleTest.finish();
+}
+
+</script>
+
+<body onload="window.setTimeout(runTests, 0);">
+
+<img style="width: 100px; height: 100px;"
+ src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"
+ id="handling_target"
+ ondragenter="event.preventDefault()"
+ ondragover="event.preventDefault()"
+ ondrop="gGotHandlingDrop = true;">
+
+<img style="width: 100px; height: 100px;"
+ src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"
+ id="nothandling_target"
+ ondrop="gGotNotHandlingDrop = true;">
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug517851.html b/dom/events/test/test_bug517851.html
new file mode 100644
index 0000000000..a550ea55be
--- /dev/null
+++ b/dom/events/test/test_bug517851.html
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=517851
+-->
+<head>
+ <title>Test for Bug 517851</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=517851">Mozilla Bug 517851</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe id="subframe"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 517851 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+window.handledCount = 0;
+window.testReturnValue = false;
+var target = document.createElement("div");
+var target2 = $("subframe").contentDocument.body;
+target.setAttribute("onerror", "++window.handledCount; return window.testReturnValue;");
+target2.setAttribute("onerror", "++window.parent.handledCount; return window.parent.testReturnValue;");
+target.setAttribute("onmouseover", "++window.handledCount; return window.testReturnValue;");
+target.setAttribute("onbeforeunload", "++window.handledCount; return window.testReturnValue;");
+target2.setAttribute("onbeforeunload", "++window.parent.handledCount; return window.parent.testReturnValue;");
+target.setAttribute("onmousemove", "++window.handledCount; return window.testReturnValue;");
+
+var e = new ErrorEvent("error", {bubbles: true, cancelable: true});
+window.testReturnValue = true;
+is(target.dispatchEvent(e), window.testReturnValue,
+ "error event should not have reverse return value handling on div!");
+is(handledCount, 1, "Wrong event count!");
+window.testReturnValue = false;
+is(target.dispatchEvent(e), window.testReturnValue,
+ "error event should not have reverse return value handling on div (2)!");
+is(handledCount, 2, "Wrong event count!");
+
+var e = new ErrorEvent("error", {bubbles: true, cancelable: true});
+window.testReturnValue = false;
+is(target2.dispatchEvent(e), !window.testReturnValue,
+ "error event should have reverse return value handling!");
+is(handledCount, 3, "Wrong event count!");
+window.testReturnValue = true;
+is(target2.dispatchEvent(e), !window.testReturnValue,
+ "error event should have reverse return value handling (2)!");
+is(handledCount, 4, "Wrong event count!");
+
+e = document.createEvent("MouseEvent");
+e.initEvent("mouseover", true, true);
+window.testReturnValue = true;
+is(target.dispatchEvent(e), window.testReturnValue,
+ "mouseover event should not have reverse return value handling!");
+is(handledCount, 5, "Wrong event count!");
+window.testReturnValue = false;
+is(target.dispatchEvent(e), window.testReturnValue,
+ "mouseover event should not have reverse return value handling (2)!");
+is(handledCount, 6, "Wrong event count!");
+
+e = document.createEvent("BeforeUnloadEvent");
+e.initEvent("beforeunload", true, true);
+window.testReturnValue = true;
+is(target.dispatchEvent(e), true,
+ "beforeunload event on random element should not be prevented!");
+is(handledCount, 6, "Wrong event count; handler should not have run!");
+is(target2.dispatchEvent(e), false,
+ "beforeunload event should be prevented!");
+is(handledCount, 7, "Wrong event count!");
+window.testReturnValue = false;
+is(target.dispatchEvent(e), false,
+ "beforeunload event on random element should be prevented because the event was already cancelled!");
+is(handledCount, 7, "Wrong event count; handler should not have run! (2)");
+
+e = document.createEvent("BeforeUnloadEvent");
+e.initEvent("beforeunload", true, true);
+window.testReturnValue = false;
+is(target.dispatchEvent(e), true,
+ "beforeunload event on random element should not be prevented (2)!");
+is(handledCount, 7, "Wrong event count; handler should not have run! (2)");
+
+is(target2.dispatchEvent(e), false,
+ "beforeunload event should be prevented (2)!");
+is(handledCount, 8, "Wrong event count!");
+
+// Create normal event for beforeunload.
+e = document.createEvent("Event");
+e.initEvent("beforeunload", true, true);
+window.testReturnValue = true;
+is(target.dispatchEvent(e), true,
+ "beforeunload event shouldn't be prevented (3)!");
+is(handledCount, 8, "Wrong event count: handler should not have run(3)!");
+is(target2.dispatchEvent(e), true,
+ "beforeunload event shouldn't be prevented (3)!");
+is(handledCount, 9, "Wrong event count!");
+
+e = document.createEvent("MouseEvent");
+e.initEvent("mousemove", true, true);
+window.testReturnValue = true;
+is(target.dispatchEvent(e), window.testReturnValue,
+ "mousemove event shouldn't have reverse return value handling!");
+is(handledCount, 10, "Wrong event count!");
+window.testReturnValue = false;
+is(target.dispatchEvent(e), window.testReturnValue,
+ "mousemove event shouldn't have reverse return value handling (2)!");
+is(handledCount, 11, "Wrong event count!");
+
+// Now unhook the beforeunload handler in the subframe, so we don't prompt to
+// unload.
+target2.onbeforeunload = null;
+
+SimpleTest.finish();
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug524674.xhtml b/dom/events/test/test_bug524674.xhtml
new file mode 100644
index 0000000000..d1ed83d8b1
--- /dev/null
+++ b/dom/events/test/test_bug524674.xhtml
@@ -0,0 +1,130 @@
+<?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=524674
+-->
+<window title="Mozilla Bug 524674"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- 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=524674"
+ target="_blank">Mozilla Bug 524674</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 524674 **/
+
+ var els = Cc["@mozilla.org/eventlistenerservice;1"]
+ .getService(Ci.nsIEventListenerService);
+
+ function dummyListener() {}
+
+ var runningTest = null;
+ var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
+ var xhr = new XMLHttpRequest();
+
+ // Test also double removals and such.
+ var tests = [
+ function() {
+ els.addListenerChangeListener(changeListener);
+ d.addEventListener("foo", dummyListener);
+ d.addEventListener("foo", dummyListener);
+ xhr.addEventListener("foo", dummyListener);
+ tests[0] = [{target: d, listeners: ["onfoo"]},
+ {target: xhr, listeners: ["onfoo"]}];
+ },
+ function() {
+ d.addEventListener("bar", dummyListener);
+ d.addEventListener("baz", dummyListener);
+ xhr.addEventListener("bar", dummyListener);
+ xhr.addEventListener("baz", dummyListener);
+ tests[0] = [{target: d, listeners: ["onbaz", "onbar"]},
+ {target: xhr, listeners: ["onbaz", "onbar"]}];
+ },
+ function() {
+ d.onclick = dummyListener;
+ d.onclick = dummyListener;
+ xhr.onload = dummyListener;
+ tests[0] = [{target: d, listeners: ["onclick"]},
+ {target: xhr, listeners: ["onload"]}];
+ },
+ function() {
+ d.onclick = function() {};
+ tests[0] = [{target: d, listeners: ["onclick"]}];
+ },
+ function() {
+ d.removeEventListener("foo", dummyListener);
+ d.removeEventListener("foo", dummyListener);
+ xhr.removeEventListener("foo", dummyListener);
+ tests[0] = [{target: d, listeners: ["onfoo"]},
+ {target: xhr, listeners: ["onfoo"]}];
+ },
+ function() {
+ d.removeEventListener("bar", dummyListener);
+ d.removeEventListener("baz", dummyListener);
+ xhr.removeEventListener("bar", dummyListener);
+ xhr.removeEventListener("baz", dummyListener);
+ tests[0] = [{target: d, listeners: ["onbar", "onbaz"]},
+ {target: xhr, listeners: ["onbar", "onbaz"]}];
+ },
+ function() {
+ d.onclick = null;
+ d.onclick = null;
+ xhr.onload = null;
+ tests[0] = [{target: d, listeners: ["onclick"]},
+ {target: xhr, listeners: ["onload"]}];
+ },
+ function() {
+ els.removeListenerChangeListener(changeListener);
+ // Check that once we've removed the change listener, it isn't called anymore.
+ d.addEventListener("foo", dummyListener);
+ xhr.addEventListener("foo", dummyListener);
+ SimpleTest.executeSoon(function() {
+ SimpleTest.finish();
+ });
+ }
+ ];
+
+ SimpleTest.executeSoon(tests[0]);
+
+ function changeListener(array) {
+ if (typeof tests[0] == "function") {
+ return;
+ }
+ var expectedEventChanges = tests[0];
+ var eventChanges = array.enumerate();
+ var i = 0;
+ while (eventChanges.hasMoreElements() && i < expectedEventChanges.length) {
+ var current;
+ try {
+ current = eventChanges.getNext().QueryInterface(Ci.nsIEventListenerChange);
+ var expected = expectedEventChanges[i];
+
+ if (current.target == expected.target) {
+ is(current.target, expected.target, current.target + " = " + expected.target);
+ ++i;
+ }
+ } catch(ex) {
+ continue;
+ }
+ }
+ if (expectedEventChanges.length != i) {
+ return;
+ }
+
+ is(expectedEventChanges.length, i, "Should have got notification for all the changes.");
+ tests.shift();
+
+ ok(tests.length);
+ SimpleTest.executeSoon(tests[0]);
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ ]]>
+ </script>
+</window>
diff --git a/dom/events/test/test_bug534833.html b/dom/events/test/test_bug534833.html
new file mode 100644
index 0000000000..d8b2000f25
--- /dev/null
+++ b/dom/events/test/test_bug534833.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=534833
+-->
+<head>
+ <title>Test for Bug 534833</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=534833">Mozilla Bug 534833</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 534833 **/
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(runTests);
+
+var input1GotClick = 0;
+var input2GotClick = 0;
+var textarea1GotClick = 0;
+var textarea2GotClick = 0;
+var div1GotClick = 0;
+var div2GotClick = 0;
+
+var tests = [ { element: "text", clickText: true },
+ { element: "text2", clickText: false },
+ { element: "area", clickText: true },
+ { element: "area2", clickText: false },
+ { element: "d", clickText: true },
+ { element: "d", clickText: false },
+ { element: "d2", clickText: true },
+ { element: "d2", clickText: false }
+ ];
+
+function nextTest_() {
+ if (!tests.length) {
+ finishTests();
+ return;
+ }
+
+ var test = tests.shift();
+ var el = document.getElementById(test.element);
+ el.scrollIntoView(true);
+ if (test.clickText) {
+ synthesizeMouse(el, 5, 5, {type : "mousedown" });
+ synthesizeMouse(el, 5, 5, {type : "mouseup" });
+ } else {
+ synthesizeMouse(el, el.getBoundingClientRect().width - 5, 5, {type : "mousedown" });
+ synthesizeMouse(el, el.getBoundingClientRect().width - 5, 5, {type : "mouseup" });
+ }
+ nextTest();
+}
+
+function nextTest() {
+ var el = document.getElementById("initialfocus");
+
+ el.addEventListener("focus", function() {
+ setTimeout(nextTest_, 0);
+ }, {once: true});
+ el.focus();
+}
+
+function runTests() {
+ var t = document.getElementById("text");
+ var t2 = document.getElementById("text2");
+ var a = document.getElementById("area");
+ var a2 = document.getElementById("area2");
+ var d = document.getElementById("d");
+ var d2 = document.getElementById("d2");
+
+ // input 1
+ t.onfocus = function(e) {
+ t.value = "";
+ }
+ t.onclick = function(e) {
+ ++input1GotClick;
+ }
+
+ // input 2
+ t2.onfocus = function(e) {
+ t2.value = "";
+ }
+ t2.onclick = function(e) {
+ ++input2GotClick;
+ }
+
+ // textarea 1
+ a.onfocus = function(e) {
+ a.value = "";
+ }
+ a.onclick = function(e) {
+ ++textarea1GotClick;
+ }
+
+ // textarea 2
+ a2.onfocus = function(e) {
+ a2.value = "";
+ }
+ a2.onclick = function(e) {
+ ++textarea2GotClick;
+ }
+
+ // div 1
+ var c = 0;
+ d.onmousedown = function(e) {
+ d.textContent = (++c) + " / click before or after |";
+ }
+ d.onclick = function(e) {
+ ++div1GotClick;
+ }
+
+ // div 2
+ var c2 = 0;
+ d2.onmousedown = function(e) {
+ d2.firstChild.data = (++c2) + " / click before or after |";
+ }
+ d2.onclick = function(e) {
+ ++div2GotClick;
+ }
+ nextTest();
+}
+
+function finishTests() {
+ is(input1GotClick, 1, "input element should have got a click!");
+ is(input2GotClick, 1, "input element should have got a click! (2)");
+ is(textarea1GotClick, 1, "textarea element should have got a click!");
+ is(textarea2GotClick, 1, "textarea element should have got a click! (2)");
+ is(div1GotClick, 2, "div element's content text was replaced, it should have got 2 click!");
+ is(div2GotClick, 2, "div element's content text was modified, it should have got 2 clicks!");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<input type="text" id="initialfocus"><br>
+<input type="text" id="text" value="click before |" style="width: 95%;"><br>
+<input type="text" id="text2" value="click after |" style="width: 95%;">
+<br>
+<textarea id="area" rows="2" style="width: 95%;">
+ click before
+ |
+</textarea><br>
+<textarea id="area2" rows="2" style="width: 95%;">
+ click after |
+</textarea>
+<div id="d" style="border: 1px solid black;">click before or after |</div>
+<div id="d2" style="border: 1px solid black;">click before or after |</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug545268.html b/dom/events/test/test_bug545268.html
new file mode 100644
index 0000000000..da4d0d1649
--- /dev/null
+++ b/dom/events/test/test_bug545268.html
@@ -0,0 +1,129 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=545268
+-->
+<head>
+ <title>Test for Bug 545268</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=545268">Mozilla Bug 545268</a>
+<p id="display">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 545268
+ Like the test for bug 493251, but we test that suppressing events in
+ a parent window stops the events from reaching the child window. */
+
+ var win;
+ var subwin;
+
+ var mouseDown = 0;
+ var mouseUp = 0;
+ var mouseClick = 0;
+
+ var keyDown = 0;
+ var keyPress = 0;
+ var keyUp = 0;
+
+ function doTest() {
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+ var f = win.document.getElementById("f");
+ subwin = f.contentWindow;
+ subwin.document.getElementsByTagName("input")[0].focus();
+ subwin.addEventListener("keydown", function(e) { ++keyDown; }, true);
+ subwin.addEventListener("keypress", function(e) { ++keyPress; }, true);
+ subwin.addEventListener("keyup", function(e) { ++keyUp; }, true);
+ subwin.addEventListener("mousedown", function(e) { ++mouseDown; }, true);
+ subwin.addEventListener("mouseup", function(e) { ++mouseUp; }, true);
+ subwin.addEventListener("click", function(e) { ++mouseClick; }, true);
+
+ synthesizeKey("a", {}, subwin);
+ is(keyDown, 1, "Wrong number events (1)");
+ is(keyPress, 1, "Wrong number events (2)");
+ is(keyUp, 1, "Wrong number events (3)");
+
+ // Test that suppressing events on the parent window prevents key
+ // events in the subdocument window
+ utils.suppressEventHandling(true);
+ synthesizeKey("a", {}, subwin);
+ is(keyDown, 1, "Wrong number events (4)");
+ is(keyPress, 1, "Wrong number events (5)");
+ is(keyUp, 1, "Wrong number events (6)");
+ utils.suppressEventHandling(false);
+ is(keyDown, 1, "Wrong number events (7)");
+ is(keyPress, 1, "Wrong number events (8)");
+ is(keyUp, 1, "Wrong number events (9)");
+
+ setTimeout(continueTest1, 0);
+ }
+
+ function continueTest1() {
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+ subwin.addEventListener("keydown", () => { utils.suppressEventHandling(true); }, {once: true});
+ synthesizeKey("a", {}, subwin);
+ is(keyDown, 2, "Wrong number events (10)");
+ is(keyPress, 1, "Wrong number events (11)");
+ is(keyUp, 1, "Wrong number events (12)");
+ utils.suppressEventHandling(false);
+ setTimeout(continueTest2, 0);
+ }
+
+ function continueTest2() {
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+ is(keyDown, 2, "Wrong number events (13)");
+ is(keyPress, 2, "Wrong number events (14)");
+ is(keyUp, 2, "Wrong number events (15)");
+
+ utils.sendMouseEvent("mousedown", 5, 5, 0, 1, 0);
+ utils.sendMouseEvent("mouseup", 5, 5, 0, 1, 0);
+ is(mouseDown, 1, "Wrong number events (16)");
+ is(mouseUp, 1, "Wrong number events (17)");
+ is(mouseClick, 1, "Wrong number events (18)");
+
+ utils.suppressEventHandling(true);
+ utils.sendMouseEvent("mousedown", 5, 5, 0, 1, 0);
+ utils.sendMouseEvent("mouseup", 5, 5, 0, 1, 0);
+ utils.suppressEventHandling(false);
+ is(mouseDown, 1, "Wrong number events (19)");
+ is(mouseUp, 1, "Wrong number events (20)");
+ is(mouseClick, 1, "Wrong number events (21)");
+
+ setTimeout(continueTest3, 0);
+ }
+
+ function continueTest3() {
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+ utils.sendMouseEvent("mousedown", 5, 5, 0, 1, 0);
+ utils.suppressEventHandling(true);
+ utils.sendMouseEvent("mouseup", 5, 5, 0, 1, 0);
+ utils.suppressEventHandling(false);
+ setTimeout(continueTest4, 1000);
+ }
+
+ function continueTest4() {
+ is(mouseDown, 2, "Wrong number events (19)");
+ is(mouseUp, 2, "Wrong number events (20)");
+ is(mouseClick, 2, "Wrong number events (21)");
+ win.close();
+ SimpleTest.finish();
+ }
+
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.requestFlakyTimeout("untriaged");
+ win = window.open("bug545268.html", "" , "");
+ win.onload = doTest;
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug547996-1.html b/dom/events/test/test_bug547996-1.html
new file mode 100644
index 0000000000..6ff112c555
--- /dev/null
+++ b/dom/events/test/test_bug547996-1.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=547996
+-->
+<head>
+ <title>Test for Bug 547996</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=547996">Mozilla Bug 547996</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 547996 **/
+/* mouseEvent.mozInputSource attribute */
+
+function prepareListener(eventName, expectedValue) {
+ return function(event) {
+ is(event.mozInputSource, expectedValue, "Correct .mozInputSource value in " + eventName);
+ };
+}
+
+const INPUT_SOURCE_UNKNOWN = MouseEvent.MOZ_SOURCE_UNKNOWN;
+const INPUT_SOURCE_KEYBOARD = MouseEvent.MOZ_SOURCE_KEYBOARD;
+
+function doTest() {
+ var eventNames = [
+ "mousedown",
+ "mouseup",
+ "click",
+ "dblclick",
+ "contextmenu",
+ "DOMMouseScroll",
+ "dragdrop",
+ "dragstart",
+ "dragend",
+ "dragenter",
+ "dragleave",
+ "dragover"
+ ];
+
+ var target = document.getElementById("testTarget");
+
+ for (var i in eventNames) {
+ for(var value = INPUT_SOURCE_UNKNOWN; value <= INPUT_SOURCE_KEYBOARD; value++) {
+ var eventName = eventNames[i];
+ var listener = prepareListener(eventName, value);
+
+ target.addEventListener(eventName, listener);
+
+ var newEvent = document.createEvent("MouseEvent");
+ newEvent.initNSMouseEvent(eventName, true, true, window, 0, 0, 0, 0, 0,
+ false, false, false, false, 0, null, 0, value);
+ target.dispatchEvent(newEvent);
+ target.removeEventListener(eventName, listener);
+ }
+
+ // Events created by script that do not initialize the mozInputSource
+ // value should have the value MOZ_SOURCE_UNKNOWN
+ var listener = prepareListener(eventName, INPUT_SOURCE_UNKNOWN);
+ target.addEventListener(eventName, listener);
+
+ var newEvent = document.createEvent("MouseEvent");
+ newEvent.initMouseEvent(eventName, true, true, window, 0, 0, 0, 0, 0,
+ false, false, false, false, 0, null);
+ target.dispatchEvent(newEvent);
+ target.removeEventListener(eventName, listener);
+
+ }
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+<span id="testTarget" style="border: 1px solid black;">testTarget</span>
+</body>
+</html>
diff --git a/dom/events/test/test_bug547996-2.xhtml b/dom/events/test/test_bug547996-2.xhtml
new file mode 100644
index 0000000000..e3f45bdb97
--- /dev/null
+++ b/dom/events/test/test_bug547996-2.xhtml
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=547996
+-->
+<head>
+ <title>Test for Bug 547996</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=547996">Mozilla Bug 547996</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script type="application/javascript"><![CDATA[
+
+/** Test for Bug 547996 **/
+/* mouseEvent.mozInputSource attribute */
+
+var expectedInputSource = null;
+
+function check(event) {
+ is(event.mozInputSource, expectedInputSource, ".mozInputSource");
+}
+
+function doTest() {
+ setup();
+
+ expectedInputSource = MouseEvent.MOZ_SOURCE_KEYBOARD;
+ testKeyboard();
+
+ expectedInputSource = MouseEvent.MOZ_SOURCE_MOUSE;
+ testMouse();
+
+ expectedInputSource = MouseEvent.MOZ_SOURCE_UNKNOWN;
+ testScriptedClicks();
+
+ cleanup();
+ SimpleTest.finish();
+}
+
+function testKeyboard() {
+
+ $("inputTarget").focus();
+ synthesizeKey("VK_SPACE", {});
+ synthesizeKey("VK_RETURN", {});
+
+ $("buttonTarget").focus();
+ synthesizeKey("VK_SPACE", {});
+ synthesizeKey("VK_RETURN", {});
+
+ //XUL buttons do not generate click on ENTER or SPACE,
+ //they do only on accessKey
+
+ $("anchorTarget").focus();
+ synthesizeKey("VK_RETURN", {});
+
+ synthesizeKey("VK_TAB", {});
+ synthesizeKey("VK_SPACE", {});
+ synthesizeKey("VK_RIGHT", {});
+
+ $("checkboxTarget").focus();
+ synthesizeKey("VK_SPACE", {});
+
+ var accessKeyDetails = (navigator.platform.includes("Mac")) ?
+ { ctrlKey : true } : { altKey : true, shiftKey: true };
+
+ synthesizeKey("o", accessKeyDetails);
+ synthesizeKey("t", accessKeyDetails);
+}
+
+function testMouse() {
+ synthesizeMouse($("inputTarget"), 0, 0, {});
+ synthesizeMouse($("buttonTarget"), 0, 0, {});
+ synthesizeMouse($("anchorTarget"), 0, 0, {});
+ synthesizeMouse($("radioTarget1"), 0, 0, {});
+ synthesizeMouse($("radioTarget2"), 0, 0, {});
+ synthesizeMouse($("checkboxTarget"), 0, 0, {});
+}
+
+function testScriptedClicks() {
+ $("inputTarget").click();
+ $("buttonTarget").click();
+}
+
+function setup() {
+ $("inputTarget").addEventListener("click", check);
+ $("buttonTarget").addEventListener("click", check);
+ $("anchorTarget").addEventListener("click", check);
+ $("radioTarget1").addEventListener("click", check);
+ $("radioTarget2").addEventListener("click", check);
+ $("checkboxTarget").addEventListener("click", check);
+
+}
+
+function cleanup() {
+ $("inputTarget").removeEventListener("click", check);
+ $("buttonTarget").removeEventListener("click", check);
+ $("anchorTarget").removeEventListener("click", check);
+ $("radioTarget1").removeEventListener("click", check);
+ $("radioTarget2").removeEventListener("click", check);
+ $("checkboxTarget").removeEventListener("click", check);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(doTest, window);
+
+]]></script>
+</pre>
+<input type="checkbox" id="checkboxTarget">Checkbox target</input>
+<input id="inputTarget" type="button" value="HTML Input" accesskey="o"/>
+<button id="buttonTarget">HTML Button</button>
+<a href="#" id="anchorTarget">Anchor</a>
+<input type="radio" id="radioTarget1" name="group">Radio Target 1</input>
+<input type="radio" id="radioTarget2" name="group">Radio Target 2</input>
+</body>
+</html>
diff --git a/dom/events/test/test_bug547996-3.xhtml b/dom/events/test/test_bug547996-3.xhtml
new file mode 100644
index 0000000000..cf227c21d2
--- /dev/null
+++ b/dom/events/test/test_bug547996-3.xhtml
@@ -0,0 +1,66 @@
+<?xml version="1.0"?>
+<html xmlns="http://www.w3.org/1999/xhtml"
+ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=547996
+-->
+<head>
+ <title>Test for Bug 547996</title>
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+ <script src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
+ <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=547996">Mozilla Bug 547996</a>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>//<![CDATA[
+
+/** Test for Bug 547996 **/
+/* mouseEvent.inputSource attribute */
+
+var expectedInputSource = null;
+
+function check(event) {
+ is(event.inputSource, expectedInputSource, ".inputSource");
+}
+
+function doTest() {
+ setup();
+
+ expectedInputSource = MouseEvent.MOZ_SOURCE_MOUSE;
+ testMouse();
+
+ expectedInputSource = MouseEvent.MOZ_SOURCE_UNKNOWN;
+ testScriptedClicks();
+
+ cleanup();
+ SimpleTest.finish();
+}
+
+function testMouse() {
+ synthesizeMouse($("xulButtonTarget"), 0, 0, {});
+}
+
+function testScriptedClicks() {
+ $("xulButtonTarget").click();
+}
+
+function setup() {
+ $("xulButtonTarget").addEventListener("click", check);
+}
+
+function cleanup() {
+ $("xulButtonTarget").removeEventListener("click", check);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(doTest, window);
+
+//]]>
+</script>
+</pre>
+<xul:button id="xulButtonTarget" accesskey="t">XUL Button</xul:button>
+</body>
+</html>
diff --git a/dom/events/test/test_bug556493.html b/dom/events/test/test_bug556493.html
new file mode 100644
index 0000000000..e2b106942e
--- /dev/null
+++ b/dom/events/test/test_bug556493.html
@@ -0,0 +1,74 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=556493
+-->
+<head>
+ <title>Test for Bug 556493</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>
+ div {
+ border: 1px solid;
+ }
+ </style>
+</head>
+<body onload="setTimeout(runTest, 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=556493">Mozilla Bug 556493</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 556493 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var downCount = 0;
+var upCount = 0;
+var clickCount = 0;
+function runTest() {
+ var d0 = document.getElementById("d0");
+ var d1 = document.getElementById("d1");
+ var d2 = document.getElementById("d2");
+
+ d0.onmousedown = function(e) { ++downCount; };
+ d0.onmouseup = function(e) { ++upCount; }
+ d0.onclick = function(e) { ++clickCount; }
+
+ synthesizeMouse(d1, 3, 3, { type: "mousedown"});
+ synthesizeMouse(d1, 3, 3, { type: "mouseup"});
+
+ is(downCount, 1, "Wrong mousedown event count!");
+ is(upCount, 1, "Wrong mouseup event count!");
+ is(clickCount, 1, "Wrong click event count!");
+
+ synthesizeMouse(d1, 3, 3, { type: "mousedown"});
+ synthesizeMouse(d1, 30, 3, { type: "mouseup"});
+
+ is(downCount, 2, "Wrong mousedown event count!");
+ is(upCount, 2, "Wrong mouseup event count!");
+ is(clickCount, 2, "Wrong click event count!");
+
+ synthesizeMouse(d1, 3, 3, { type: "mousedown"});
+ synthesizeMouse(d2, 3, 3, { type: "mouseup"});
+
+ is(downCount, 3, "Wrong mousedown event count!");
+ is(upCount, 3, "Wrong mouseup event count!");
+ is(clickCount, 3, "Wrong click event count!");
+
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<div id="d0">
+Test divs --
+<div id="d1">t</div><div id="d2">t</div>
+--
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug563329.html b/dom/events/test/test_bug563329.html
new file mode 100644
index 0000000000..fd4e9fd8a6
--- /dev/null
+++ b/dom/events/test/test_bug563329.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=563329
+-->
+<head>
+ <title>Test for Bug 563329</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=563329">Mozilla Bug 563329</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 563329 **/
+/* ui.click_hold_context_menus preference */
+
+var target = null;
+var testGen = getTests();
+var currentTest = null;
+
+function* getTests() {
+ let tests = [
+ { "func": function() { setTimeout(doCheckContextMenu, 100)}, "message": "Context menu should has fired"},
+ { "func": function() { setTimeout(doCheckDuration, 100)}, "message": "Context menu should has fired with delay"},
+ { "func": function() { setTimeout(finishTest, 100)}, "message": "" }
+ ];
+
+ let i = 0;
+ while (i < tests.length)
+ yield tests[i++];
+}
+
+function doTest() {
+ target = document.getElementById("testTarget");
+
+ document.documentElement.addEventListener("contextmenu", function() {
+ SimpleTest.ok(true, currentTest.message);
+ synthesizeMouse(target, 0, 0, {type: "mouseup"});
+ SimpleTest.executeSoon(function() {
+ currentTest = testGen.next();
+ currentTest.func();
+ });
+ });
+
+ SimpleTest.executeSoon(function() {
+ currentTest = testGen.next();
+ currentTest.func();
+ });
+}
+
+function doCheckContextMenu() {
+ synthesizeMouse(target, 0, 0, {type: "mousedown"});
+}
+
+function doCheckDuration() {
+ var duration = 50;
+
+ // Change click hold delay
+ SpecialPowers.pushPrefEnv({"set":[["ui.click_hold_context_menus.delay", duration]]}, function() { synthesizeMouse(target, 0, 0, {type: "mousedown"}); });
+}
+
+function finishTest() {
+ synthesizeKey("VK_ESCAPE", {}, window);
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+ SpecialPowers.pushPrefEnv({"set":[["ui.click_hold_context_menus", true]]}, doTest);
+});
+</script>
+</pre>
+<span id="testTarget" style="border: 1px solid black;">testTarget</span>
+</body>
+</html>
diff --git a/dom/events/test/test_bug574663.html b/dom/events/test/test_bug574663.html
new file mode 100644
index 0000000000..332bddbf1a
--- /dev/null
+++ b/dom/events/test/test_bug574663.html
@@ -0,0 +1,194 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=574663
+-->
+<head>
+ <title>Test for Bug 574663</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=574663">Mozilla Bug 574663</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script>
+
+/** Test for Bug 574663 **/
+
+// SimpleTest's paint_listener does not work on other windows, so we inline
+// a smaller version here.
+function waitForPaint(win, utils, callback) {
+ win.document.documentElement.getBoundingClientRect();
+ if (!utils.isMozAfterPaintPending) {
+ win.requestAnimationFrame(function() {
+ setTimeout(callback);
+ });
+ return;
+ }
+
+ var onpaint = function() {
+ if (!utils.isMozAfterPaintPending) {
+ win.removeEventListener("MozAfterPaint", onpaint);
+ callback();
+ return;
+ }
+ if (utils.isTestControllingRefreshes) {
+ utils.advanceTimeAndRefresh(0);
+ }
+ }
+ win.addEventListener("MozAfterPaint", onpaint);
+ if (utils.isTestControllingRefreshes) {
+ utils.advanceTimeAndRefresh(0);
+ }
+}
+
+// This is a magic number representing how many device pixels we are attempting to
+// scroll or zoom. We use it for sending the wheel events, but we don't actually
+// check that we have scrolled by that amount.
+var kDelta = 3;
+
+function sendTouchpadScrollMotion(scrollbox, direction, ctrl, momentum, callback) {
+ var win = scrollbox.ownerDocument.defaultView;
+ let winUtils = SpecialPowers.getDOMWindowUtils(win);
+
+ let event = {
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaY: direction * kDelta,
+ lineOrPageDeltaY: direction,
+ ctrlKey: ctrl,
+ isMomentum: momentum
+ };
+
+ // Construct a promise that will resolve when either scroll or zoom has changed.
+ // --- Intermittent Warning ---
+ // Two wheel events are sent, but our promise resolves when any change has been
+ // made to scroll or zoom. That makes it possible that the effect of the second
+ // event may not yet be applied when the promise resolves. This shouldn't lead
+ // to any errors, since the two wheel events are moving in the same direction,
+ // and our later checks will only ensure that the value has changed from its
+ // initial value. This was done intentionally, because attempting to wait after
+ // both events yields problems when the second event has no effect, which does
+ // happen in testing. It's not clear why this is happening. Since the testing
+ // pattern is scroll (twice), then scroll back (twice), it's possible that the
+ // first scroll back event is sufficient to return the scrollbox to its minimal
+ // scrollTop position, and so the second event doesn't scroll any further.
+ const initialZoom = SpecialPowers.getFullZoom(win);
+ const initialScroll = scrollbox.scrollTop;
+
+ const effectOfWheelEvent = SimpleTest.promiseWaitForCondition(() => {
+ return ((SpecialPowers.getFullZoom(win) != initialZoom) || (scrollbox.scrollTop != initialScroll));
+ }, "Mouse wheel should have caused us to either zoom or scroll.");
+
+ synthesizeWheel(scrollbox, 10, 10, event, win);
+
+ // then additional pixel scroll
+ event.lineOrPageDeltaY = 0;
+ synthesizeWheel(scrollbox, 10, 10, event, win);
+
+ effectOfWheelEvent.then(callback);
+}
+
+function runTest() {
+ var win = open('bug574663.html', '_blank', 'width=300,height=300');
+ let winUtils = SpecialPowers.getDOMWindowUtils(win);
+
+ let waitUntilPainted = function(callback) {
+ // Until the first non-blank paint, the parent will set the opacity of our
+ // browser to 0 using the 'blank' attribute.
+ // Until the blank attribute is removed, we can't send scroll events.
+ SimpleTest.waitForFocus(function() {
+ /* eslint-disable no-shadow */
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ let win = Services.wm.getMostRecentWindow("navigator:browser");
+ win.requestAnimationFrame(() => {
+ win.gBrowser.selectedBrowser.removeAttribute("blank");
+ win.requestAnimationFrame(() => {
+ sendAsyncMessage("blank-attribute-removed");
+ });
+ });
+ });
+ /* eslint-enable no-shadow */
+ chromeScript.promiseOneMessage("blank-attribute-removed").then(() => {
+ chromeScript.destroy();
+ waitForPaint(win, winUtils, callback);
+ });
+ }, win);
+ };
+
+ waitUntilPainted(function () {
+ var scrollbox = win.document.getElementById("scrollbox");
+ let outstandingTests = [
+ [false, false],
+ [false, true],
+ [true, false],
+ [true, true],
+ ];
+
+ // grab the refresh driver, since we want to make sure
+ // async scrolls happen in deterministic time
+ winUtils.advanceTimeAndRefresh(1000);
+
+ function nextTest() {
+ let [ctrlKey, isMomentum] = outstandingTests.shift();
+ let scrollTopBefore = scrollbox.scrollTop;
+ let zoomFactorBefore = SpecialPowers.getFullZoom(win);
+
+ let check = function() {
+ if (!ctrlKey) {
+ let postfix = isMomentum ? ", even after releasing the touchpad" : "";
+ // Normal scroll: scroll
+ is(SpecialPowers.getFullZoom(win), zoomFactorBefore, "Normal scrolling shouldn't change zoom" + postfix);
+ isnot(scrollbox.scrollTop, scrollTopBefore,
+ "Normal scrolling should scroll" + postfix);
+ } else {
+ if (!isMomentum) {
+ isnot(SpecialPowers.getFullZoom(win), zoomFactorBefore, "Ctrl-scrolling should zoom while the user is touching the touchpad");
+ is(scrollbox.scrollTop, scrollTopBefore, "Ctrl-scrolling shouldn't scroll while the user is touching the touchpad");
+ } else {
+ is(SpecialPowers.getFullZoom(win), zoomFactorBefore, "Momentum scrolling shouldn't zoom, even when pressing Ctrl");
+ isnot(scrollbox.scrollTop, scrollTopBefore,
+ "Momentum scrolling should scroll, even when pressing Ctrl");
+ }
+ }
+
+ if (!outstandingTests.length) {
+ winUtils.restoreNormalRefresh();
+ win.close();
+ SimpleTest.finish();
+ return;
+ }
+
+ // Revert the effect for the next test.
+ sendTouchpadScrollMotion(scrollbox, -1, ctrlKey, isMomentum, function() {
+ setTimeout(nextTest, 0);
+ });
+ }
+
+ sendTouchpadScrollMotion(scrollbox, 1, ctrlKey, isMomentum, check);
+ }
+ nextTest();
+ });
+}
+
+window.onload = function() {
+ SpecialPowers.pushPrefEnv({
+ "set":[["general.smoothScroll", false],
+ ["mousewheel.acceleration.start", -1],
+ ["mousewheel.system_scroll_override.enabled", false],
+ ["mousewheel.with_control.action", 3],
+ ["test.events.async.enabled", true]]}, runTest);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug586961.xhtml b/dom/events/test/test_bug586961.xhtml
new file mode 100644
index 0000000000..ebdcc248d6
--- /dev/null
+++ b/dom/events/test/test_bug586961.xhtml
@@ -0,0 +1,46 @@
+<?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=586961
+-->
+<window title="Mozilla Bug 586961"
+ 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"/>
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=586961">Mozilla Bug 586961</a>
+
+ <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<box onclick="clicked(event)">
+ <label id="controllabel" control="controlbutton" accesskey="k" value="Click here" />
+ <button id="controlbutton" label="Button" />
+</box>
+
+<script class="testbody" type="application/javascript"><![CDATA[
+
+/** Test for Bug 586961 **/
+
+function clicked(event) {
+ is(event.target.id, "controlbutton", "Accesskey was directed to controlled element.");
+ SimpleTest.finish();
+}
+
+function test() {
+ var accessKeyDetails = (navigator.platform.includes("Mac")) ?
+ { altKey : true, ctrlKey : true } :
+ { altKey : true, shiftKey: true };
+ synthesizeKey("k", accessKeyDetails);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(test, window);
+
+]]></script>
+
+</window>
diff --git a/dom/events/test/test_bug591249.xhtml b/dom/events/test/test_bug591249.xhtml
new file mode 100644
index 0000000000..c95bf501de
--- /dev/null
+++ b/dom/events/test/test_bug591249.xhtml
@@ -0,0 +1,73 @@
+<?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=591249
+-->
+<window title="Mozilla Bug 591249" onload="RunTest()"
+ 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"/>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=591249">Mozilla Bug 591249</a>
+ <img id="image"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC"
+ ondragstart="event.preventDefault();"/>
+ <iframe id="iframe" src="chrome://mochitests/content/chrome/dom/events/test/bug591249_iframe.xhtml" style="height: 300px; width: 100%;"></iframe>
+</body>
+
+<script class="testbody" type="application/javascript"><![CDATA[
+/** Test for Bug 591249 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function completeTest(aBox) {
+ ok(window.getComputedStyle(aBox).backgroundColor == "rgb(255, 0, 0)", "The -moz-drag-over style should be removed.");
+ SimpleTest.finish();
+}
+
+function fireEvent(target, event) {
+ var win = target.ownerGlobal;
+ var utils = win.windowUtils;
+ utils.dispatchDOMEventViaPresShellForTesting(target, event);
+}
+
+function RunTest() {
+ var image = document.getElementById("image");
+ var iframe = document.getElementById("iframe");
+ var iBox = iframe.contentDocument.getElementById("drop-target");
+ var insideBoxX = iBox.offsetWidth + 10;
+ var insideBoxY = iBox.offsetHeight + 10;
+
+ var dataTransfer;
+ var trapDrag = function(event) {
+ dataTransfer = event.dataTransfer;
+ dataTransfer.setData("text/plain", "Hello");
+ dataTransfer.dropEffect = "move";
+ event.preventDefault();
+ event.stopPropagation();
+ }
+
+ // need to use real mouse action to get the dataTransfer
+ window.addEventListener("dragstart", trapDrag, true);
+ synthesizeMouse(image, 2, 2, { type: "mousedown" });
+ synthesizeMouse(image, 11, 11, { type: "mousemove" });
+ synthesizeMouse(image, 20, 20, { type: "mousemove" });
+ window.removeEventListener("dragstart", trapDrag, true);
+ synthesizeMouse(image, 20, 20, { type: "mouseup" });
+
+ var event = document.createEvent("DragEvent");
+ event.initDragEvent("dragover", true, true, iBox.ownerGlobal, 0, 0, 0, 0, 0, false, false, false, false, 0, iBox, dataTransfer);
+ fireEvent(iBox, event);
+ synthesizeMouse(image, 3, 3, { type: "mousedown" });
+ synthesizeMouse(image, 23, 23, { type: "mousemove" });
+ synthesizeMouse(iBox, insideBoxX, insideBoxY, { type: "mousemove" });
+ ok(window.getComputedStyle(iBox).backgroundColor == "rgb(255, 255, 0)", "The -moz-drag-over style should be applied.");
+ synthesizeMouse(iBox, insideBoxX, insideBoxY, { type: "mouseup" });
+ window.setTimeout(function () { completeTest(iBox); }, 40);
+}
+]]></script>
+
+</window>
diff --git a/dom/events/test/test_bug591815.html b/dom/events/test/test_bug591815.html
new file mode 100644
index 0000000000..c3987bb957
--- /dev/null
+++ b/dom/events/test/test_bug591815.html
@@ -0,0 +1,68 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=591815
+-->
+<head>
+ <title>Test for Bug 591815</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 onload="setTimeout(runTest, 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=591815">Mozilla Bug 591815</a>
+<p id="display"></p>
+<div id="content">
+ <div id="wrapper">
+ <!-- 20x20 of red -->
+ <img id="image" ondragstart="fail();"
+ src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC"/>
+ </div>
+</div>
+<pre id="test">
+
+<script type="application/javascript">
+
+/** Test for Bug 591815 **/
+
+SimpleTest.waitForExplicitFinish();
+
+function fail() {
+ ok(false, "drag started but should not have");
+}
+
+function runTest() {
+ var image = document.getElementById("image");
+ var wrapper = document.getElementById("wrapper");
+ var preventDefault = function(event) {
+ event.preventDefault();
+ };
+ wrapper.addEventListener('mousedown', preventDefault);
+
+ synthesizeMouse(image, 3, 3, { type: "mousedown"});
+ synthesizeMouse(image, 53, 53, { type: "mousemove"});
+ synthesizeMouse(image, 53, 53, { type: "mouseup"});
+
+ wrapper.removeEventListener('mousedown', preventDefault);
+
+ var relocateElementAndPreventDefault = function(event) {
+ document.body.appendChild(wrapper);
+ event.preventDefault();
+ }
+ wrapper.addEventListener('mousedown', relocateElementAndPreventDefault);
+
+ synthesizeMouse(image, 3, 3, { type: "mousedown"});
+ synthesizeMouse(image, 53, 53, { type: "mousemove"});
+ synthesizeMouse(image, 53, 53, { type: "mouseup"});
+
+ wrapper.removeEventListener('mousedown', relocateElementAndPreventDefault);
+
+ ok(true, "passed the test");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug593959.html b/dom/events/test/test_bug593959.html
new file mode 100644
index 0000000000..a809e028c3
--- /dev/null
+++ b/dom/events/test/test_bug593959.html
@@ -0,0 +1,60 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=593959
+-->
+<head>
+ <title>Test for Bug 593959</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>
+ body:active {
+ background: red;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=593959">Mozilla Bug 593959</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 593959 **/
+
+ function doTest() {
+ var utils = SpecialPowers.getDOMWindowUtils(window);
+ var e = document.createEvent("MouseEvent");
+ e.initEvent("mousedown", false, false, window, 0, 1, 1, 1, 1,
+ false, false, false, false, 0, null);
+ utils.dispatchDOMEventViaPresShellForTesting(document.body, e);
+
+ is(document.querySelector("body:active"), document.body, "body should be active!")
+
+ var ifrwindow = document.getElementById("ifr").contentWindow;
+
+ var utils2 = SpecialPowers.getDOMWindowUtils(ifrwindow);
+
+ var e2 = ifrwindow.document.createEvent("MouseEvent");
+ e2.initEvent("mouseup", false, false, ifrwindow, 0, 1, 1, 1, 1,
+ false, false, false, false, 0, null);
+ utils2.dispatchDOMEventViaPresShellForTesting(ifrwindow.document.body, e2);
+
+ isnot(document.querySelector("body:active"), document.body, "body shouldn't be active!")
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(doTest);
+
+
+
+</script>
+</pre>
+<iframe id="ifr"></iframe>
+</body>
+</html>
diff --git a/dom/events/test/test_bug602962.xhtml b/dom/events/test/test_bug602962.xhtml
new file mode 100644
index 0000000000..221bc51ed5
--- /dev/null
+++ b/dom/events/test/test_bug602962.xhtml
@@ -0,0 +1,85 @@
+<?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=602962
+-->
+<window title="Mozilla Bug 602962" onload="openWindow()"
+ 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">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=602962">Mozilla Bug 602962</a>
+ <p id="display"></p>
+<div id="content" style="display: none">
+</div>
+</body>
+
+<script class="testbody" type="application/javascript"><![CDATA[
+/** Test for Bug 602962 **/
+var scrollbox, content;
+var scrollX = 0, scrollY = 0;
+
+var oldWidth = 0, oldHeight = 0;
+var win = null;
+
+function openWindow() {
+ win = window.open("chrome://mochitests/content/chrome/dom/events/test/bug602962.xhtml", "_blank", "width=600,height=600");
+}
+
+function doTest() {
+ scrollbox = win.document.getElementById("page-scrollbox");
+ content = win.document.getElementById("page-box");
+ content.style.width = 400 + "px";
+
+ win.addEventListener("resize", function() {
+ setTimeout(function(){
+ scrollbox.scrollBy(200,0);
+ resize();
+ },0);
+ }, { once: true });
+
+ oldWidth = win.outerWidth;
+ oldHeight = win.outerHeight;
+ win.resizeTo(200, 400);
+}
+
+function resize() {
+ scrollX = scrollbox.scrollLeft;
+ scrollY = scrollbox.scrollTop;
+
+ win.addEventListener("resize", function() {
+ content.style.width = (oldWidth + 400) + "px";
+ win.removeEventListener("resize", arguments.callee, true);
+
+ setTimeout(function() {
+ finish();
+ }, 0);
+ }, true);
+
+ win.resizeTo(oldWidth, oldHeight);
+}
+
+function finish() {
+ if (win.outerWidth != oldWidth ||
+ win.outerHeight != oldHeight) {
+ // We should eventually get back to the original size.
+ setTimeout(finish, 0);
+ return;
+ }
+ scrollbox.scrollBy(scrollX, scrollY);
+
+ is(scrollbox.scrollLeft, 200, "Scroll Left should have been restored to the value before the resize");
+ is(scrollbox.scrollTop, 0, "Scroll Top should have been restored to the value before the resize");
+
+ is(win.outerWidth, oldWidth, "Width should be resized");
+ is(win.outerHeight, oldHeight, "Height should be resized");
+ win.close();
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+]]></script>
+
+</window>
diff --git a/dom/events/test/test_bug603008.html b/dom/events/test/test_bug603008.html
new file mode 100644
index 0000000000..ccba289a51
--- /dev/null
+++ b/dom/events/test/test_bug603008.html
@@ -0,0 +1,559 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=508906
+-->
+<head>
+ <title>Test for Bug 603008</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=508906">Mozilla Bug 603008</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 306008 - Touch* Events **/
+
+let tests = [], testTarget, parent;
+
+let touch = {
+ id: 0,
+ point: {x: 0, y: 0},
+ radius: {x: 0, y: 0},
+ rotation: 0,
+ force: 0.5,
+ target: null
+}
+
+function nextTest() {
+ if (tests.length)
+ SimpleTest.executeSoon(tests.shift());
+}
+
+function random() {
+ return Math.floor(Math.random() * 100);
+}
+
+function checkEvent(aFakeEvent) {
+ return function(aEvent) {
+ is(aFakeEvent.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey");
+ is(aFakeEvent.altKey, aEvent.altKey, "Correct altKey");
+ is(aFakeEvent.shiftKey, aEvent.shiftKey, "Correct shiftKey");
+ is(aFakeEvent.metaKey, aEvent.metaKey, "Correct metaKey");
+ checkTouches(aFakeEvent.touches, aEvent.touches);
+ checkTouches(aFakeEvent.targetTouches, aEvent.targetTouches);
+ checkTouches(aFakeEvent.changedTouches, aEvent.changedTouches);
+ }
+}
+
+function checkTouches(aTouches1, aTouches2) {
+ is(aTouches1.length, aTouches2.length, "Correct touches length");
+ for (var i = 0; i < aTouches1.length; i++) {
+ checkTouch(aTouches1[i], aTouches2[i]);
+ }
+}
+
+function checkTouch(aFakeTouch, aTouch) {
+ is(aFakeTouch.identifier, aTouch.identifier, "Touch has correct identifier");
+ is(aFakeTouch.target, aTouch.target, "Touch has correct target");
+ is(aFakeTouch.page.x, aTouch.pageX, "Touch has correct pageX");
+ is(aFakeTouch.page.y, aTouch.pageY, "Touch has correct pageY");
+ is(aFakeTouch.page.x + Math.round(window.mozInnerScreenX), aTouch.screenX, "Touch has correct screenX");
+ is(aFakeTouch.page.y + Math.round(window.mozInnerScreenY), aTouch.screenY, "Touch has correct screenY");
+ is(aFakeTouch.page.x, aTouch.clientX, "Touch has correct clientX");
+ is(aFakeTouch.page.y, aTouch.clientY, "Touch has correct clientY");
+ is(aFakeTouch.radius.x, aTouch.radiusX, "Touch has correct radiusX");
+ is(aFakeTouch.radius.y, aTouch.radiusY, "Touch has correct radiusY");
+ is(aFakeTouch.rotationAngle, aTouch.rotationAngle, "Touch has correct rotationAngle");
+ is(aFakeTouch.force, aTouch.force, "Touch has correct force");
+}
+
+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 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 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 testSingleTouch(name) {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target = document.getElementById("testTarget");
+ let target2 = document.getElementById("testTarget2");
+ let bcr = target.getBoundingClientRect();
+ let bcr2 = target2.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target
+ });
+ let event = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ // test touchstart event fires correctly
+ var checkFunction = checkEvent(event);
+ window.addEventListener("touchstart", checkFunction);
+ sendTouchEvent(cwu, "touchstart", event, 0);
+ window.removeEventListener("touchstart", checkFunction);
+
+ // test touchmove event fires correctly
+ event.touches[0].page.x -= 1;
+ event.targetTouches[0].page.x -= 1;
+ event.changedTouches[0].page.x -= 1;
+ checkFunction = checkEvent(event);
+ window.addEventListener("touchmove", checkFunction);
+ sendTouchEvent(cwu, "touchmove", event, 0);
+ window.removeEventListener("touchmove", checkFunction);
+
+ // test touchend event fires correctly
+ event.touches = [];
+ event.targetTouches = [];
+ checkFunction = checkEvent(event);
+ window.addEventListener("touchend", checkFunction);
+ sendTouchEvent(cwu, "touchend", event, 0);
+ window.removeEventListener("touchend", checkFunction);
+
+ nextTest();
+}
+
+function testSingleTouch2(name) {
+ // firing a touchstart that includes only one touch will evict any touches in the queue with touchend messages
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target = document.getElementById("testTarget");
+ let target2 = document.getElementById("testTarget2");
+ let bcr = target.getBoundingClientRect();
+ let bcr2 = target2.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ identifier: 0,
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target
+ });
+ let event1 = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+ let touch2 = new testtouch({
+ identifier: 1,
+ page: {x: Math.round(bcr2.left + bcr2.width/2),
+ y: Math.round(bcr2.top + bcr2.height/2)},
+ target: target2
+ });
+ let event2 = new touchEvent({
+ touches: [touch2],
+ targetTouches: [touch2],
+ changedTouches: [touch2]
+ });
+
+ // test touchstart event fires correctly
+ var checkFunction1 = checkEvent(event1);
+ window.addEventListener("touchstart", checkFunction1);
+ sendTouchEvent(cwu, "touchstart", event1, 0);
+ window.removeEventListener("touchstart", checkFunction1);
+
+ event1.touches = [];
+ event1.targetTouches = [];
+ checkFunction1 = checkEvent(event1);
+ var checkFunction2 = checkEvent(event2);
+
+ window.addEventListener("touchend", checkFunction1);
+ window.addEventListener("touchstart", checkFunction2);
+ sendTouchEvent(cwu, "touchstart", event2, 0);
+ window.removeEventListener("touchend", checkFunction1);
+ window.removeEventListener("touchstart", checkFunction2);
+
+ sendTouchEvent(cwu, "touchstart", event1, 0);
+
+ nextTest();
+}
+
+
+function testMultiTouch(name) {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target1 = document.getElementById("testTarget");
+ let target2 = document.getElementById("testTarget2");
+ let bcr = target1.getBoundingClientRect();
+ let bcr2 = target2.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ identifier: 0,
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target: target1
+ });
+ let touch2 = new testtouch({
+ identifier: 1,
+ page: {x: Math.round(bcr2.left + bcr2.width/2),
+ y: Math.round(bcr2.top + bcr2.height/2)},
+ target: target2
+ });
+ let event = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ // test touchstart event fires correctly
+ var checkFunction = checkEvent(event);
+ window.addEventListener("touchstart", checkFunction);
+ sendTouchEvent(cwu, "touchstart", event, 0);
+ window.removeEventListener("touchstart", checkFunction);
+
+ event.touches.push(touch2);
+ event.targetTouches = [touch2];
+ event.changedTouches = [touch2];
+ window.addEventListener("touchstart", checkFunction);
+ sendTouchEvent(cwu, "touchstart", event, 0);
+ window.removeEventListener("touchstart", checkFunction);
+
+ // test moving one touch point
+ event.touches[0].page.x -= 1;
+ event.targetTouches = [event.touches[0]];
+ event.changedTouches = [event.touches[0]];
+ window.addEventListener("touchmove", checkFunction);
+ sendTouchEvent(cwu, "touchmove", event, 0);
+ window.removeEventListener("touchmove", checkFunction);
+
+ // test moving both touch points -- two touchmove events should fire, one on each target
+ event.touches[0].page.x -= 1;
+ event.touches[1].page.x -= 1;
+ event.targetTouches = event.touches;
+ event.changedTouches = event.touches;
+ var touchMoveEvents = 0;
+ var checkFunction2 = function(aEvent) {
+ is(event.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey");
+ is(event.altKey, aEvent.altKey, "Correct altKey");
+ is(event.shiftKey, aEvent.shiftKey, "Correct shiftKey");
+ is(event.metaKey, aEvent.metaKey, "Correct metaKey");
+ checkTouches(event.touches, aEvent.touches);
+ checkTouches(event.changedTouches, aEvent.changedTouches);
+ if (aEvent.targetTouches[0].target == target1) {
+ checkTouches([event.touches[0]], aEvent.targetTouches);
+ } else if (aEvent.targetTouches[0].target == target2) {
+ checkTouches([event.touches[1]], aEvent.targetTouches);
+ } else
+ ok(false, "Event target is incorrect: " + event.targetTouches[0].target.nodeName + "#" + event.targetTouches[0].target.id);
+ touchMoveEvents++;
+ };
+ window.addEventListener("touchmove", checkFunction2);
+ sendTouchEvent(cwu, "touchmove", event, 0);
+ is(touchMoveEvents, 2, "Correct number of touchmove events fired");
+ window.removeEventListener("touchmove", checkFunction2);
+
+ // test removing just one finger
+ var expected = new touchEvent({
+ touches: [touch2],
+ targetTouches: [],
+ changedTouches: [touch1]
+ });
+ checkFunction = checkEvent(expected);
+
+ event.touches = [];
+ event.targetTouches = [];
+ event.changedTouches = [touch1];
+
+ // test removing the other finger
+ window.addEventListener("touchend", checkFunction);
+ sendTouchEvent(cwu, "touchend", event, 0);
+ window.removeEventListener("touchend", checkFunction);
+
+ event.touches = [];
+ event.targetTouches = [];
+ event.changedTouches = [touch2];
+ checkFunction = checkEvent(event);
+ window.addEventListener("touchend", checkFunction);
+ sendTouchEvent(cwu, "touchend", event, 0);
+ window.removeEventListener("touchend", checkFunction);
+
+ nextTest();
+}
+
+function testTouchChanged() {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target1 = document.getElementById("testTarget");
+ let bcr = target1.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ identifier: 0,
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target: target1
+ });
+ let event = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ var checkFunction = checkEvent(event);
+ sendTouchEvent(cwu, "touchstart", event, 0);
+
+ var moveEvents = 0;
+ function onMove(aEvent) {
+ moveEvents++;
+ }
+
+ window.addEventListener("touchmove", onMove);
+
+ // the first touchmove should always fire an event
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // changing nothing should not fire a touchmove event
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // test moving x
+ event.touches[0].page.x -= 1;
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // test moving y
+ event.touches[0].page.y -= 1;
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // test changing y radius
+ event.touches[0].radius.y += 1;
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // test changing x radius
+ event.touches[0].radius.x += 1;
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // test changing rotationAngle
+ event.touches[0].rotationAngle += 1;
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // test changing force
+ event.touches[0].force += 1;
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ // changing nothing again
+ sendTouchEvent(cwu, "touchmove", event, 0);
+
+ is(moveEvents, 7, "Six move events fired");
+
+ window.removeEventListener("touchmove", onMove);
+ sendTouchEvent(cwu, "touchend", event, 0);
+ nextTest();
+}
+
+function testPreventDefault() {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target = document.getElementById("testTarget");
+ let target2 = document.getElementById("testTarget2");
+ let bcr = target.getBoundingClientRect();
+ let bcr2 = target2.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ page: {x: bcr.left + bcr.width/2,
+ y: bcr.top + bcr.height/2},
+ target
+ });
+ let event = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ let preventFunction = function(aEvent) {
+ aEvent.preventDefault();
+ }
+
+ let subTests = [
+ [{ name: "touchstart", prevent: false },
+ { name: "touchmove", prevent: false },
+ { name: "touchmove", prevent: false },
+ { name: "touchend", prevent: false }],
+ [{ name: "touchstart", prevent: true, doPrevent: true },
+ { name: "touchmove", prevent: false },
+ { name: "touchmove", prevent: false },
+ { name: "touchend", prevent: false }],
+ [{ name: "touchstart", prevent: false },
+ { name: "touchmove", prevent: true, doPrevent: true },
+ { name: "touchmove", prevent: false },
+ { name: "touchend", prevent: false }],
+ [{ name: "touchstart", prevent: false },
+ { name: "touchmove", prevent: false },
+ { name: "touchmove", prevent: false, doPrevent: true },
+ { name: "touchend", prevent: false }],
+ [{ name: "touchstart", prevent: false },
+ { name: "touchmove", prevent: false },
+ { name: "touchmove", prevent: false },
+ { name: "touchend", prevent: true, doPrevent: true }]
+ ];
+
+ var dotest = function(aTest) {
+ if (aTest.doPrevent) {
+ target.addEventListener(aTest.name, preventFunction);
+ }
+
+ if (aTest.name == "touchmove") {
+ touch1.page.x++;
+ event.touches[0] = touch1;
+ }
+
+ is(sendTouchEvent(cwu, aTest.name, event, 0), aTest.prevent, "Got correct status");
+
+ if (aTest.doPrevent)
+ target.removeEventListener(aTest.name, preventFunction);
+ }
+
+ for (var i = 0; i < subTests.length; i++) {
+ for (var j = 0; j < subTests[i].length; j++) {
+ dotest(subTests[i][j]);
+ }
+ }
+
+ nextTest();
+}
+
+function testRemovingElement() {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target = document.getElementById("testTarget");
+ let bcr = document.getElementById("testTarget").getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ page: {x: bcr.left + bcr.width/2,
+ y: bcr.top + bcr.height/2},
+ });
+ let e = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ var touchEvents = 0;
+ var removeTarget = function(aEvent) {
+ aEvent.target.remove();
+ };
+
+ var checkTarget = function(aEvent) {
+ is(aEvent.target, target, "Event has correct target");
+ touchEvents++;
+ };
+
+ target.addEventListener("touchstart", removeTarget);
+ target.addEventListener("touchmove", checkTarget);
+ target.addEventListener("touchend", checkTarget);
+
+ sendTouchEvent(cwu, "touchstart", e, 0);
+
+ e.touches[0].page.x++;
+ sendTouchEvent(cwu, "touchmove", e, 0);
+ sendTouchEvent(cwu, "touchend", e, 0);
+
+ target.removeEventListener("touchstart", removeTarget);
+ target.removeEventListener("touchmove", checkTarget);
+ target.removeEventListener("touchend", checkTarget);
+
+ is(touchEvents, 2, "Check target was called twice");
+
+ nextTest();
+}
+
+function testNAC() {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target = document.getElementById("testTarget3");
+ let bcr = target.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target
+ });
+ let event = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ // test touchstart event fires correctly
+ var checkFunction = checkEvent(event);
+ window.addEventListener("touchstart", checkFunction);
+ sendTouchEvent(cwu, "touchstart", event, 0);
+ window.removeEventListener("touchstart", checkFunction);
+
+ sendTouchEvent(cwu, "touchend", event, 0);
+
+ nextTest();
+}
+
+function doTest() {
+ tests.push(testSingleTouch);
+ tests.push(testSingleTouch2);
+ tests.push(testMultiTouch);
+ tests.push(testPreventDefault);
+ tests.push(testTouchChanged);
+ tests.push(testRemovingElement);
+ tests.push(testNAC);
+
+ tests.push(function() {
+ SimpleTest.finish();
+ });
+
+ nextTest();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+<div id="parent">
+ <span id="testTarget" style="padding: 5px; border: 1px solid black;">testTarget</span>
+ <span id="testTarget2" style="padding: 5px; border: 1px solid blue;">testTarget</span>
+ <input type="text" id="testTarget3">
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug605242.html b/dom/events/test/test_bug605242.html
new file mode 100644
index 0000000000..732501f558
--- /dev/null
+++ b/dom/events/test/test_bug605242.html
@@ -0,0 +1,58 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=605242
+-->
+<head>
+ <title>Test for Bug 605242</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="setTimeout('runTest()', 0)">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=605242">Mozilla Bug 605242</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 605242 **/
+
+SimpleTest.waitForExplicitFinish();
+
+var utils = SpecialPowers.getDOMWindowUtils(window);
+function sendMouseDown(el) {
+ var rect = el.getBoundingClientRect();
+ utils.sendMouseEvent('mousedown', rect.left + 5, rect.top + 5, 0, 1, 0);
+}
+
+function sendMouseUp(el) {
+ var rect = el.getBoundingClientRect();
+ utils.sendMouseEvent('mouseup', rect.left + 5, rect.top + 5, 0, 1, 0);
+}
+
+function runTest() {
+ var b = document.getElementById("testbutton");
+ sendMouseDown(b);
+ var l = document.querySelectorAll(":active");
+
+ var contains = false;
+ for (var i = 0; i < l.length; ++i) {
+ if (l[i] == b) {
+ contains = true;
+ }
+ }
+
+ ok(contains, "Wrong active content! \n");
+ sendMouseUp(b);
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+<button id="testbutton">A button</button>
+<pre id="log">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug607464.html b/dom/events/test/test_bug607464.html
new file mode 100644
index 0000000000..b9a176ae33
--- /dev/null
+++ b/dom/events/test/test_bug607464.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=607464
+-->
+<head>
+ <title>Test for Bug 607464</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=607464">Mozilla Bug 607464</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/**
+ * Test for Bug 607464:
+ * Pixel scrolling shouldn't scroll smoothly, even if general.smoothScroll is on.
+ **/
+
+function scrollDown150PxWithPixelScrolling(scrollbox) {
+ var win = scrollbox.ownerDocument.defaultView;
+ let event = {
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaY: 30.0,
+ lineOrPageDeltaY: 1
+ };
+ // A pixel scroll with lineOrPageDeltaY.
+ synthesizeWheel(scrollbox, 10, 10, event, win);
+ // then 4 pixel scrolls without lineOrPageDeltaY.
+ event.lineOrPageDeltaY = 0;
+ for (let i = 0; i < 4; ++i) {
+ synthesizeWheel(scrollbox, 10, 10, event, win);
+ }
+
+ // Note: the line scroll shouldn't have any effect because it has
+ // hasPixels = true set on it. We send it to emulate normal
+ // behavior.
+}
+
+function runTest() {
+ var win = open('bug607464.html', '_blank', 'width=300,height=300');
+ SimpleTest.waitForFocus(function () {
+ var scrollbox = win.document.getElementById("scrollbox");
+ let scrollTopBefore = scrollbox.scrollTop;
+
+ win.addEventListener("scroll", function(e) {
+ is(scrollbox.scrollTop % 30, 0,
+ "Pixel scrolling should happen instantly, not smoothly. The " +
+ "scroll position " + scrollbox.scrollTop + " in this sequence of wheel " +
+ "events should be a multiple of 30.");
+ if (scrollbox.scrollTop == 150) {
+ win.close();
+ SimpleTest.finish();
+ }
+ }, true);
+
+ promiseOnlyApzControllerFlushed(win).then(function() {
+ scrollDown150PxWithPixelScrolling(scrollbox);
+ });
+ }, win);
+}
+
+window.onload = function() {
+ SpecialPowers.pushPrefEnv({
+ "set":[["general.smoothScroll", true],
+ ["mousewheel.acceleration.start", -1],
+ ["mousewheel.system_scroll_override.enabled", false]]}, runTest);
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.testInChaosMode();
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug613634.html b/dom/events/test/test_bug613634.html
new file mode 100644
index 0000000000..18205d23df
--- /dev/null
+++ b/dom/events/test/test_bug613634.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=613634
+-->
+<head>
+ <title>Test for Bug 613634</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=613634">Mozilla Bug 613634</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 613634 **/
+
+var eventCount = 0;
+function l(e) {
+ if (e.eventPhase != Event.CAPTURING_PHASE) {
+ ++eventCount;
+ } else {
+ ok(false, "Listener shouldn't be called!");
+ }
+}
+
+var d1 = document.createElement("div");
+var d2 = document.createElement("div");
+
+d1.appendChild(d2);
+
+var x = new XMLHttpRequest();
+
+try {
+d1.addEventListener("foo", l);
+document.addEventListener("foo", l);
+window.addEventListener("foo", l);
+x.addEventListener("foo", l);
+} catch(ex) {
+ ok(false, "Shouldn't throw " + ex);
+}
+
+var ev = document.createEvent("Event");
+ev.initEvent("foo", true, true);
+d2.dispatchEvent(ev);
+is(eventCount, 1, "Event listener should have been called!");
+
+ev = document.createEvent("Event");
+ev.initEvent("foo", false, false);
+d2.dispatchEvent(ev);
+is(eventCount, 1, "Event listener shouldn't have been called!");
+
+d1.removeEventListener("foo", l);
+ev = document.createEvent("Event");
+ev.initEvent("foo", true, true);
+d2.dispatchEvent(ev);
+is(eventCount, 1, "Event listener shouldn't have been called!");
+
+
+ev = document.createEvent("Event");
+ev.initEvent("foo", true, true);
+document.body.dispatchEvent(ev);
+is(eventCount, 3, "Event listener should have been called on document and window!");
+
+document.removeEventListener("foo", l);
+window.removeEventListener("foo", l);
+ev = document.createEvent("Event");
+ev.initEvent("foo", true, true);
+document.body.dispatchEvent(ev);
+is(eventCount, 3, "Event listener shouldn't have been called on document and window!");
+
+ev = document.createEvent("Event");
+ev.initEvent("foo", true, true);
+x.dispatchEvent(ev);
+is(eventCount, 4, "Event listener should have been called on XMLHttpRequest!");
+
+x.removeEventListener("foo", l);
+ev = document.createEvent("Event");
+ev.initEvent("foo", true, true);
+x.dispatchEvent(ev);
+is(eventCount, 4, "Event listener shouldn't have been called on XMLHttpRequest!");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug615597.html b/dom/events/test/test_bug615597.html
new file mode 100644
index 0000000000..ddca4a6869
--- /dev/null
+++ b/dom/events/test/test_bug615597.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=615597
+-->
+<head>
+ <title>Test for Bug 615597</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=615597">Mozilla Bug 615597</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 615597 **/
+
+window.addEventListener("deviceorientation", function(event) {
+ is(event.alpha, 1.5);
+ is(event.beta, 2.25);
+ is(event.gamma, 3.667);
+ is(event.absolute, true);
+}, true);
+
+var event = DeviceOrientationEvent;
+ok(!!event, "Should have seen DeviceOrientationEvent!");
+
+event = document.createEvent("DeviceOrientationEvent");
+event.initDeviceOrientationEvent('deviceorientation', true, true, 1.5, 2.25, 3.667, true);
+window.dispatchEvent(event);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug617528.xhtml b/dom/events/test/test_bug617528.xhtml
new file mode 100644
index 0000000000..ec2d58cadd
--- /dev/null
+++ b/dom/events/test/test_bug617528.xhtml
@@ -0,0 +1,96 @@
+<?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=617528
+-->
+<window title="Mozilla Bug 617528"
+ 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">
+ <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=617528"
+ target="_blank">Mozilla Bug 617528</a>
+ </body>
+
+ <script type="application/javascript"><![CDATA[
+ const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+ );
+ var _window;
+ var browser;
+
+ function start() {
+ _window = window.browsingContext.topChromeWindow.open("window_bug617528.xhtml", "_new", "chrome");
+ _window.addEventListener("load", onLoad);
+ }
+
+ function onLoad() {
+ _window.removeEventListener("load", onLoad);
+
+ browser = _window.document.getElementById("browser");
+ browser.addEventListener("pageshow", onPageShow);
+
+ var uri='data:text/html,\
+<html>\
+ <body>\
+ <div oncontextmenu="event.preventDefault()">\
+ <input id="node" type="text" value="Click here"></input>\
+ </div>\
+ </body>\
+</html>';
+ BrowserTestUtils.startLoadingURIString(browser, uri);
+ }
+
+ function onPageShow() {
+ browser.removeEventListener("pageshow", onPageShow, true);
+ SimpleTest.executeSoon(doTest);
+ }
+
+ function onContextMenu1(event) {
+ is(event.defaultPrevented, true,
+ "expected event.defaultPrevented to be true (1)");
+ is(event.target.localName, "input",
+ "expected event.target.localName to be 'input' (1)");
+ is(event.originalTarget.localName, "div",
+ "expected event.originalTarget.localName to be 'div' (1)");
+ }
+
+ function onContextMenu2(event) {
+ is(event.defaultPrevented, false,
+ "expected event.defaultPrevented to be false (2)");
+ is(event.target.localName, "input",
+ "expected event.target.localName to be 'input' (2)");
+ is(event.originalTarget.localName, "div",
+ "expected event.originalTarget.localName to be 'div' (2)");
+ }
+
+ function doTest() {
+ var win = browser.contentWindow;
+ win.focus();
+ var node = win.document.getElementById("node");
+ var rect = node.getBoundingClientRect();
+ var left = rect.left + rect.width / 2;
+ var top = rect.top + rect.height / 2;
+
+ var wu = win.windowUtils;
+
+ browser.addEventListener("contextmenu", onContextMenu1);
+ wu.sendMouseEvent("contextmenu", left, top, 2, 1, 0);
+ browser.removeEventListener("contextmenu", onContextMenu1);
+
+ browser.addEventListener("contextmenu", onContextMenu2);
+ var shiftMask = Event.SHIFT_MASK;
+ wu.sendMouseEvent("contextmenu", left, top, 2, 1, shiftMask);
+ browser.removeEventListener("contextmenu", onContextMenu2);
+
+ _window.close();
+ SimpleTest.finish();
+ }
+
+ addLoadEvent(start);
+ SimpleTest.waitForExplicitFinish();
+ ]]></script>
+</window>
diff --git a/dom/events/test/test_bug624127.html b/dom/events/test/test_bug624127.html
new file mode 100644
index 0000000000..7099f9f936
--- /dev/null
+++ b/dom/events/test/test_bug624127.html
@@ -0,0 +1,35 @@
+<html>
+<head>
+ <title>Test for Bug 624127</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 onload="setTimeout('runTest()', 0)">
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+function runTest() {
+ synthesizeMouse($("text"), 2, 2, { type: "mousedown" });
+ synthesizeMouse(frames[0].document.body, 2, 2, { type: "mouseup" }, frames[0]);
+ synthesizeMouse($("text2"), 50, 8, { type: "mousemove" });
+
+ is(window.getSelection().toString(), "", "no selection made");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+
+<p id="text">Normal text</p>
+<iframe srcdoc="text in iframe"></iframe>
+<p id="text2">Normal text</p>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug635465.html b/dom/events/test/test_bug635465.html
new file mode 100644
index 0000000000..1e1bf7330f
--- /dev/null
+++ b/dom/events/test/test_bug635465.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=635465
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 635465</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 type="text/css">
+ #item {
+ position: relative;
+ }
+ .s-menu-section-submenu {
+ position: absolute;
+ display: none;
+ }
+ .open .s-menu-section-submenu {
+ display: block;
+ }
+</style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635465">Mozilla Bug 635465</a>
+<div id="display">
+ <div class="item" id="item"
+ onmouseover="showSubmenu(event)" onmouseout="hideSubmenu(event)">
+ <a href="#" id="firsthover">Hover me</a>
+ <div class="s-menu-section-submenu" id="menu">
+ <a href="#" id="secondhover">Now hover me</a>
+ </div>
+ </div>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 635465 **/
+function showSubmenu(event) {
+ var item = document.getElementById('item');
+
+ var width = item.offsetWidth; // IT WORKS IF YOU REMOVE THIS LINE
+
+ item.className='open';
+}
+
+function hideSubmenu(event) {
+ document.getElementById('item').className='';
+}
+
+SimpleTest.waitForExplicitFinish();
+
+function executeTests() {
+ // First flush out layout of firsthover
+ ok($("firsthover").getBoundingClientRect().height > 0,
+ "Should have a nonzero height before hover");
+
+ // Now trigger a mouseover on firsthover
+ synthesizeMouseAtCenter($("firsthover"), { type: "mousemove" });
+
+ ok($("secondhover").getBoundingClientRect().height > 0,
+ "Should have a nonzero height for submenu after hover");
+
+ // Now determine where secondhover is hanging out
+ var rect = $("secondhover").getBoundingClientRect();
+ synthesizeMouseAtCenter($("secondhover"), { type: "mousemove" });
+
+ // And another mouseover one pixel to the right of where the center used to be
+ synthesizeMouseAtPoint(rect.left + rect.width/2 + 1,
+ rect.top + rect.height/2,
+ { type: "mousemove" });
+
+ ok($("secondhover").getBoundingClientRect().height > 0,
+ "Should have a nonzero height for submenu after second hover");
+
+ // And check computed display of the menu
+ is(getComputedStyle($("menu"), "").display, "block", "Should have block display");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForFocus(executeTests);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug641477.html b/dom/events/test/test_bug641477.html
new file mode 100644
index 0000000000..b669c3145b
--- /dev/null
+++ b/dom/events/test/test_bug641477.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=641477
+-->
+<head>
+ <title>Test for Bug 641477</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=641477">Mozilla Bug 641477</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 641477 **/
+
+var didThrow = false;
+
+var e = document.createEvent("Event");
+try {
+ is(e.type, "", "Event type should be empty string before initialization");
+ document.dispatchEvent(e);
+} catch(ex) {
+ didThrow = (ex.name == "InvalidStateError" && ex.code == DOMException.INVALID_STATE_ERR);
+}
+
+ok(didThrow, "Should have thrown InvalidStateError!");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug648573.html b/dom/events/test/test_bug648573.html
new file mode 100644
index 0000000000..11348349ba
--- /dev/null
+++ b/dom/events/test/test_bug648573.html
@@ -0,0 +1,120 @@
+<!-- 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=648573
+ -->
+ <head>
+ <title>Test for Bug 648573</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=648573">Mozilla Bug 648573</a>
+ <p id="display"></p>
+ <div id="content" style="display: none">
+
+ </div>
+ <pre id="test">
+ <script type="application/javascript">
+
+ /** Test for Bug 648573 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function runTest() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ var win = iframe.contentWindow;
+ var doc = iframe.contentDocument;
+
+ var utils = SpecialPowers.getDOMWindowUtils(win);
+
+ ok("createTouch" in doc, "Should have createTouch function");
+ ok("createTouchList" in doc, "Should have createTouchList function");
+ ok(doc.createEvent("touchevent"), "Should be able to create TouchEvent objects");
+
+ var t1 = doc.createTouch(win, doc, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11);
+ is(t1.target, doc, "Wrong target");
+ is(t1.identifier, 1, "Wrong identifier");
+ is(t1.pageX, 2, "Wrong pageX");
+ is(t1.pageY, 3, "Wrong pageY");
+ is(t1.screenX, 4, "Wrong screenX");
+ is(t1.screenY, 5, "Wrong screenY");
+ is(t1.clientX, 6, "Wrong clientX");
+ is(t1.clientY, 7, "Wrong clientY");
+ is(t1.radiusX, 8, "Wrong radiusX");
+ is(t1.radiusY, 9, "Wrong radiusY");
+ is(t1.rotationAngle, 10, "Wrong rotationAngle");
+ is(t1.force, 11, "Wrong force");
+
+ var t2 = doc.createTouch();
+
+ var l1 = doc.createTouchList(t1);
+ is(l1.length, 1, "Wrong length");
+ is(l1.item(0), t1, "Wront item (1)");
+ is(l1[0], t1, "Wront item (2)");
+
+ var l2 = doc.createTouchList([t1, t2]);
+ is(l2.length, 2, "Wrong length");
+ is(l2.item(0), t1, "Wront item (3)");
+ is(l2.item(1), t2, "Wront item (4)");
+ is(l2[0], t1, "Wront item (5)");
+ is(l2[1], t2, "Wront item (6)");
+
+ var l3 = doc.createTouchList();
+
+ var e = doc.createEvent("touchevent");
+ e.initTouchEvent("touchmove", true, true, win, 0, true, true, true, true,
+ l1, l2, l3);
+ is(e.touches, l1, "Wrong list (1)");
+ is(e.targetTouches, l2, "Wrong list (2)");
+ is(e.changedTouches, l3, "Wrong list (3)");
+ ok(e.altKey, "Alt should be true");
+ ok(e.metaKey, "Meta should be true");
+ ok(e.ctrlKey, "Ctrl should be true");
+ ok(e.shiftKey, "Shift should be true");
+
+
+ var events =
+ ["touchstart",
+ "touchend",
+ "touchmove",
+ "touchcancel"];
+
+ function runEventTest(type) {
+ var event = doc.createEvent("touchevent");
+ event.initTouchEvent(type, true, true, win, 0, true, true, true, true,
+ l1, l2, l3);
+ var t = doc.createElement("div");
+ // Testing target.onFoo;
+ var didCall = false;
+ t["on" + type] = function (evt) {
+ is(evt, event, "Wrong event");
+ evt.target.didCall = true;
+ }
+ t.dispatchEvent(event);
+ ok(t.didCall, "Should have called the listener(1)");
+
+ // Testing <element onFoo="">
+ t = doc.createElement("div");
+ t.setAttribute("on" + type, "this.didCall = true;");
+ t.dispatchEvent(event);
+ ok(t.didCall, "Should have called the listener(2)");
+ }
+
+ for (var i = 0; i < events.length; ++i) {
+ runEventTest(events[i]);
+ }
+
+ SimpleTest.finish();
+ }
+
+ SpecialPowers.pushPrefEnv(
+ {"set": [["dom.w3c_touch_events.legacy_apis.enabled", true]]}, runTest);
+ </script>
+ </pre>
+ </body>
+</html>
diff --git a/dom/events/test/test_bug650493.html b/dom/events/test/test_bug650493.html
new file mode 100644
index 0000000000..a830c688fd
--- /dev/null
+++ b/dom/events/test/test_bug650493.html
@@ -0,0 +1,215 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=650493
+-->
+<head>
+ <title>Test for Bug 650493</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=650493">Mozilla Bug 650493</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+function getNodes() {
+ var walker = document.createTreeWalker($('content'), NodeFilter.SHOW_ALL, null);
+ var nodes = [];
+ do {
+ nodes.push(walker.currentNode);
+ } while(walker.nextNode());
+
+ return nodes;
+}
+
+function check() {
+ var current = getNodes();
+ is(nodes.length, current.length, "length after " + testName);
+ nodes.forEach(function(val, index) {
+ ok(current.indexOf(val) > -1, "nodes[" + index + "] (" + val + ") shouldn't exist after " + testName);
+ });
+}
+
+var nodes = getNodes();
+var testName = "empty";
+var mutateCount = 0;
+
+check();
+
+// Set up listeners
+root = $('content');
+root.addEventListener("DOMNodeInserted", function(e) {
+ mutateCount++;
+ is(e.isTrusted, true, "untrusted mutation event");
+ var w = document.createTreeWalker(e.target, NodeFilter.SHOW_ALL, null);
+ do {
+ is(nodes.indexOf(w.currentNode), -1, "already have inserted node (" + w.currentNode + ") when " + testName);
+ nodes.push(w.currentNode);
+ } while(w.nextNode());
+});
+root.addEventListener("DOMNodeRemoved", function(e) {
+ mutateCount++;
+ is(e.isTrusted, true, "untrusted mutation event");
+ var w = document.createTreeWalker(e.target, NodeFilter.SHOW_ALL, null);
+ do {
+ var index = nodes.indexOf(w.currentNode);
+ ok(index != -1, "missing removed node (" + w.currentNode + ") when " + testName);
+ nodes.splice(index, 1);
+ } while(w.nextNode());
+});
+
+testName = "text-only innerHTML";
+root.innerHTML = "hello world";
+check();
+
+testName = "innerHTML with <b>";
+root.innerHTML = "<b>bold</b> world";
+check();
+
+testName = "complex innerHTML";
+root.innerHTML = "<b>b<span>old</span></b> <strong>world";
+check();
+
+testName = "replacing using .textContent";
+root.textContent = "i'm just a plain text minding my own business";
+check();
+
+testName = "clearing using .textContent";
+root.textContent = "";
+check();
+
+testName = "inserting using .textContent";
+root.textContent = "i'm new text!!";
+check();
+
+testName = "inserting using .textContent";
+root.textContent = "i'm new text!!";
+check();
+
+testName = "preparing to normalize";
+root.innerHTML = "<u><b>foo</b></u> ";
+var u = root.firstChild;
+is(u.nodeName, "U", "got the right node");
+var b = u.firstChild;
+is(b.nodeName, "B", "got the right node");
+b.insertBefore(document.createTextNode(""), b.firstChild);
+b.insertBefore(document.createTextNode(""), b.firstChild);
+b.appendChild(document.createTextNode(""));
+b.appendChild(document.createTextNode("hello"));
+b.appendChild(document.createTextNode("world"));
+u.appendChild(document.createTextNode("foo"));
+u.appendChild(document.createTextNode(""));
+u.appendChild(document.createTextNode("bar"));
+check();
+
+testName = "normalizing";
+root.normalize();
+check();
+
+testName = "self replace firstChild";
+mutateCount = 0;
+root.replaceChild(root.firstChild, root.firstChild);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "self replace second child";
+mutateCount = 0;
+root.replaceChild(root.firstChild.nextSibling, root.firstChild.nextSibling);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "self replace lastChild";
+mutateCount = 0;
+root.replaceChild(root.lastChild, root.lastChild);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "self insertBefore firstChild";
+mutateCount = 0;
+root.insertBefore(root.firstChild, root.firstChild);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "self insertBefore second child";
+mutateCount = 0;
+root.insertBefore(root.firstChild.nextSibling, root.firstChild.nextSibling);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "self insertBefore lastChild";
+mutateCount = 0;
+root.insertBefore(root.lastChild, root.lastChild);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "appendChild last";
+mutateCount = 0;
+root.appendChild(root.lastChild);
+check();
+is(mutateCount, 2, "should remove and reinsert " + testName);
+
+testName = "prepare script/style";
+script = document.createElement("script");
+script.appendChild(document.createTextNode("void(0);"));
+root.appendChild(script);
+style = document.createElement("style");
+root.appendChild(style);
+check();
+
+testName = "set something in script";
+script.text = "something";
+check();
+
+testName = "set something in style";
+style.innerHTML = "something { dislay: none; }";
+check();
+
+testName = "moving style";
+root.insertBefore(style, root.firstChild);
+check();
+
+testName = "replacing script";
+root.replaceChild(b, script);
+check();
+
+testName = "doc-fragment insert in the middle";
+frag = document.createDocumentFragment();
+frag.addEventListener("DOMNodeRemoved", function(e) {
+ var index = children.indexOf(e.target);
+ ok(index != -1, "unknown child removed from fragment");
+ children.splice(index, 1);
+});
+var children = [];
+children.push(document.createTextNode("foo"));
+children.push(document.createTextNode("bar"));
+children.push(document.createElement("span"));
+children.push(document.createElement("b"));
+children[2].appendChild(document.createElement("i"));
+children.forEach(function(child) { frag.appendChild(child); });
+ok(root.firstChild, "need to have children in order to test inserting before end");
+root.replaceChild(frag, root.firstChild);
+check();
+is(children.length, 0, "should have received DOMNodeRemoved for all frag children when inserting");
+is(frag.childNodes.length, 0, "fragment should be empty when inserting");
+
+testName = "doc-fragment append at the end";
+children.push(document.createTextNode("foo"));
+children.push(document.createTextNode("bar"));
+children.push(document.createElement("span"));
+children.push(document.createElement("b"));
+children[2].appendChild(document.createElement("i"));
+children.forEach(function(child) { frag.appendChild(child); });
+root.appendChild(frag);
+check();
+is(children.length, 0, "should have received DOMNodeRemoved for all frag children when appending");
+is(frag.childNodes.length, 0, "fragment should be empty when appending");
+
+</script>
+</body>
+</html>
+
diff --git a/dom/events/test/test_bug656379-1.html b/dom/events/test/test_bug656379-1.html
new file mode 100644
index 0000000000..9eee769c04
--- /dev/null
+++ b/dom/events/test/test_bug656379-1.html
@@ -0,0 +1,30 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=656379
+-->
+<head>
+ <title>Test for Bug 656379</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"/>
+</head>
+<body>
+
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 656379 **/
+SimpleTest.waitForExplicitFinish();
+var subwindow = window.open("./bug656379-1.html", "bug656379", "width=800,height=1000");
+
+function finishTests() {
+ subwindow.close();
+ SimpleTest.finish();
+}
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug656379-2.html b/dom/events/test/test_bug656379-2.html
new file mode 100644
index 0000000000..b7dd47bbc3
--- /dev/null
+++ b/dom/events/test/test_bug656379-2.html
@@ -0,0 +1,115 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=656379
+-->
+<head>
+ <title>Test for Bug 656379</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>
+ input[type="button"]:hover { color: green; }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=656379">Mozilla Bug 656379</a>
+<p id="display">
+ <label for="button1" id="label1">Label 1</label>
+ <input type="button" id="button1" value="Button 1">
+ <label>
+ <span id="label2">Label 2</span>
+ <input type="button" id="button2" value="Button 2">
+ </label>
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+function log(aEvent) {
+ function getPath() {
+ if (!aEvent.target) {
+ return "(null)";
+ }
+ function getNodeName(aNode) {
+ if (aNode.id) {
+ return `${aNode.nodeName}#${aNode.id}`;
+ }
+ return aNode.nodeName;
+ }
+ let path = getNodeName(aEvent.target);
+ for (let parent = aEvent.target.parentElement;
+ parent && document.body != parent;
+ parent = parent.parentElement) {
+ path = `${getNodeName(parent)} > ${path}`;
+ }
+ return path;
+ }
+ info(`${aEvent.type} on ${getPath()}`);
+}
+
+window.addEventListener("mousemove", log, {capture: true});
+window.addEventListener("mouseenter", log, {capture: true});
+window.addEventListener("mouseleave", log, {capture: true});
+window.addEventListener("mouseover", log, {capture: true});
+window.addEventListener("mouseout", log, {capture: true});
+
+/** Test for Bug 656379 **/
+SimpleTest.waitForExplicitFinish();
+function* tests() {
+ info("Synthesizing mousemove on label1...");
+ synthesizeMouseAtCenter($("label1"), { type: "mousemove" });
+ yield undefined;
+ is($("button1").matches(":hover"), true,
+ "Button 1 should be hovered after mousemove over label1");
+ is($("label1").matches(":hover"), true,
+ "Label 1 should be hovered after mousemove over label1");
+ is($("button2").matches(":hover"), false,
+ "Button 2 should not be hovered after mousemove over label1");
+ is($("label2").matches(":hover"), false,
+ "Label 2 should not be hovered after mousemove over label1");
+ info("Synthesizing mousemove on button2...");
+ synthesizeMouseAtCenter($("button2"), { type: "mousemove" });
+ yield undefined;
+ is($("button1").matches(":hover"), false,
+ "Button 1 should not be hovered after mousemove over button2");
+ is($("label1").matches(":hover"), false,
+ "Label 1 should not be hovered after mousemove over button2");
+ is($("button2").matches(":hover"), true,
+ "Button 2 should be hovered after mousemove over button2");
+ is($("label2").matches(":hover"), false,
+ "Label 2 should not be hovered after mousemove over label2");
+ info("Synthesizing mousemove on label2...");
+ synthesizeMouseAtCenter($("label2"), { type: "mousemove" });
+ yield undefined;
+ is($("button1").matches(":hover"), false,
+ "Button 1 should not be hovered after mousemove over label2");
+ is($("label1").matches(":hover"), false,
+ "Label 1 should not be hovered after mousemove over label2");
+ is($("button2").matches(":hover"), true,
+ "Button 2 should be hovered after mousemove over label2");
+ is($("label2").matches(":hover"), true,
+ "Label 2 should be hovered after mousemove over label2");
+ SimpleTest.finish();
+}
+
+function executeTests() {
+ var testYielder = tests();
+ function execNext() {
+ let {done} = testYielder.next();
+ if (done) {
+ return;
+ }
+ SimpleTest.executeSoon(execNext);
+ }
+ execNext();
+}
+
+SimpleTest.waitForFocus(executeTests);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug656954.html b/dom/events/test/test_bug656954.html
new file mode 100644
index 0000000000..60f8cf4133
--- /dev/null
+++ b/dom/events/test/test_bug656954.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=656954
+-->
+<head>
+ <title>Test for Bug 656954</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=656954">Mozilla Bug 656954</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 656954 **/
+
+var e = document.createEvent("Event");
+is(e.defaultPrevented, false,
+ "After creating event defaultPrevented should be false");
+e.initEvent("foo", true, true);
+var el = document.createElement("div");
+el.addEventListener("foo",
+ function(evt) {
+ evt.preventDefault();
+ });
+el.dispatchEvent(e);
+is(e.defaultPrevented, true, "preventDefault() should have been called!");
+
+e = document.createEvent("Event");
+e.initEvent("foo", true, false);
+el.dispatchEvent(e);
+is(e.defaultPrevented, false, "preventDefault() should not have any effect!");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug659071.html b/dom/events/test/test_bug659071.html
new file mode 100644
index 0000000000..920d86da29
--- /dev/null
+++ b/dom/events/test/test_bug659071.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=659071
+-->
+<head>
+ <title>Test for Bug 659071</title>
+ <script type="text/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=659071">Mozilla Bug 659071</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 659071 **/
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var subWin = window.open("window_bug659071.html", "_blank",
+ "width=500,height=500");
+
+function finish()
+{
+ subWin.close();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug659350.html b/dom/events/test/test_bug659350.html
new file mode 100644
index 0000000000..f4d64d1f35
--- /dev/null
+++ b/dom/events/test/test_bug659350.html
@@ -0,0 +1,111 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=659350
+-->
+<head>
+ <title>Test for Bug 659350</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=659350">Mozilla Bug 659350</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 659350 **/
+function testIn(eventName, obj, objName, expected) {
+ is(eventName in obj, expected, "'" + eventName + "' shuld be in " + objName);
+}
+
+var div = document.createElement("div");
+
+// Forwarded events
+testIn("onscroll", window, "window", true);
+testIn("onscroll", document.body, "body", true);
+testIn("onscroll", div, "div", true);
+// Window events
+testIn("onpopstate", window, "window", true);
+testIn("onpopstate", document.body, "body", true);
+testIn("onpopstate", div, "div", false);
+// Non-idl events
+testIn("onopen", window, "window", false);
+testIn("onopen", document.body, "body", false);
+testIn("onopen", div, "div", false);
+
+function f() {}
+function g() {}
+
+// Basic sanity of interaction between the IDL and content attributes
+div.onload = f;
+is(div.onload, f, "Should have 'f' as div's onload");
+div.setAttribute("onload", "");
+isnot(div.onload, f, "Should not longer have 'f' as div's onload");
+is(div.onload.toString(), "function onload(event) {\n\n}",
+ "Should have wrapped empty string in a function");
+div.setAttribute("onload", "foopy();");
+is(div.onload.toString(), "function onload(event) {\nfoopy();\n}",
+ "Should have wrapped call in a function");
+div.removeAttribute("onload");
+is(div.onload, null, "Should have null onload now");
+
+// Test forwarding to window for both events that are window-specific and that
+// exist on all elements
+function testPropagationToWindow(eventName) {
+ is(window["on"+eventName], null, "Shouldn't have " + eventName + " stuff yet");
+ document.body["on"+eventName] = f;
+ is(window["on"+eventName], f,
+ "Setting on"+eventName+" on body should propagate to window");
+ document.createElement("body")["on"+eventName] = g;
+ is(window["on"+eventName], g,
+ "Setting on"+eventName+" on body not in document should propagate to window");
+ document.createElement("frameset")["on"+eventName] = f;
+ is(window["on"+eventName], f,
+ "Setting on"+eventName+" on frameset not in document should propagate to window");
+
+ document.body.setAttribute("on"+eventName, eventName);
+ is(window["on"+eventName].toString(),
+ "function on"+eventName+"(event) {\n"+eventName+"\n}",
+ "Setting on"+eventName+"attribute on body should propagate to window");
+ document.createElement("body").setAttribute("on"+eventName, eventName+"2");
+ is(window["on"+eventName].toString(),
+ "function on"+eventName+"(event) {\n"+eventName+"2\n}",
+ "Setting on"+eventName+"attribute on body outside the document should propagate to window");
+}
+
+testPropagationToWindow("popstate");
+testPropagationToWindow("scroll");
+
+// Test |this| and scoping
+var called;
+div.onscroll = function(event) {
+ is(this, div, "This should be div when invoking event listener");
+ is(event, ev, "Event argument should be the event that was dispatched");
+ called = true;
+}
+var ev = document.createEvent("Events");
+ev.initEvent("scroll", true, true);
+called = false;
+div.dispatchEvent(ev);
+is(called, true, "Event listener set via on* property not called");
+
+div.foopy = "Found me";
+document.foopy = "Didn't find me";
+document.foopy2 = "Found me";
+div.setAttribute("onscroll",
+ "is(this, div, 'This should be div when invoking via attribute');\
+ is(foopy, 'Found me', 'div should be on the scope chain when invoking handler compiled from content attribute');\
+ is(foopy2, 'Found me', 'document should be on the scope chain when invking handler compiled from content attribute');\
+ is(event, ev, 'Event argument should be the event that was dispatched');\
+ called = true;");
+called = false;
+div.dispatchEvent(ev);
+is(called, true, "Event listener set via on* attribute not called");
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug662678.html b/dom/events/test/test_bug662678.html
new file mode 100644
index 0000000000..22fe8e75a3
--- /dev/null
+++ b/dom/events/test/test_bug662678.html
@@ -0,0 +1,153 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=662678
+-->
+<head>
+ <title>Test for Bug 662678</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=662678">Mozilla Bug 662678</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 662678 **/
+SimpleTest.waitForExplicitFinish();
+
+var checkMotion = function(event) {
+ window.removeEventListener("devicemotion", checkMotion, true);
+
+ is(event.acceleration.x, 1.5, "acceleration.x");
+ is(event.acceleration.y, 2.5, "acceleration.y");
+ is(event.acceleration.z, 3.5, "acceleration.z");
+ is(event.accelerationIncludingGravity.x, 4.5, "accelerationIncludingGravity.x");
+ is(event.accelerationIncludingGravity.y, 5.5, "accelerationIncludingGravity.y");
+ is(event.accelerationIncludingGravity.z, 6.5, "accelerationIncludingGravity.z");
+ is(event.rotationRate.alpha, 7.5, "rotationRate.alpha");
+ is(event.rotationRate.beta, 8.5, "rotationRate.beta");
+ is(event.rotationRate.gamma, 9.5, "rotationRate.gamma");
+ is(event.interval, 0.5, "interval");
+
+ var e = document.createEvent("DeviceMotionEvent");
+ e.initDeviceMotionEvent('devicemotion', true, true,
+ null, null, null, null);
+ is(e.acceleration.x, null, "acceleration.x");
+ is(e.acceleration.y, null, "acceleration.y");
+ is(e.acceleration.z, null, "acceleration.z");
+ is(e.accelerationIncludingGravity.x, null, "accelerationIncludingGravity.x");
+ is(e.accelerationIncludingGravity.y, null, "accelerationIncludingGravity.y");
+ is(e.accelerationIncludingGravity.z, null, "accelerationIncludingGravity.z");
+ is(e.rotationRate.alpha, null, "rotationRate.alpha");
+ is(e.rotationRate.beta, null, "rotationRate.beta");
+ is(e.rotationRate.gamma, null, "rotationRate.gamma");
+ is(e.interval, null, "interval");
+
+ e.initDeviceMotionEvent('devicemotion', true, true,
+ {}, {}, {}, 0);
+ is(e.acceleration.x, null, "acceleration.x");
+ is(e.acceleration.y, null, "acceleration.y");
+ is(e.acceleration.z, null, "acceleration.z");
+ is(e.accelerationIncludingGravity.x, null, "accelerationIncludingGravity.x");
+ is(e.accelerationIncludingGravity.y, null, "accelerationIncludingGravity.y");
+ is(e.accelerationIncludingGravity.z, null, "accelerationIncludingGravity.z");
+ is(e.rotationRate.alpha, null, "rotationRate.alpha");
+ is(e.rotationRate.beta, null, "rotationRate.beta");
+ is(e.rotationRate.gamma, null, "rotationRate.gamma");
+ is(e.interval, 0, "interval");
+
+ window.addEventListener("devicemotion", checkMotionCtor, true);
+
+ event = new DeviceMotionEvent('devicemotion', {
+ bubbles: true, cancelable: true,
+ acceleration: {x:1.5,y:2.5,z:3.5},
+ accelerationIncludingGravity: {x:4.5,y:5.5,z:6.5},
+ rotationRate: {alpha:7.5,beta:8.5,gamma:9.5},
+ interval: 0.5
+ });
+ window.dispatchEvent(event);
+};
+
+var checkMotionCtor = function(event) {
+ window.removeEventListener("devicemotion", checkMotionCtor, true);
+
+ is(event.acceleration.x, 1.5, "acceleration.x");
+ is(event.acceleration.y, 2.5, "acceleration.y");
+ is(event.acceleration.z, 3.5, "acceleration.z");
+ is(event.accelerationIncludingGravity.x, 4.5, "accelerationIncludingGravity.x");
+ is(event.accelerationIncludingGravity.y, 5.5, "accelerationIncludingGravity.y");
+ is(event.accelerationIncludingGravity.z, 6.5, "accelerationIncludingGravity.z");
+ is(event.rotationRate.alpha, 7.5, "rotationRate.alpha");
+ is(event.rotationRate.beta, 8.5, "rotationRate.beta");
+ is(event.rotationRate.gamma, 9.5, "rotationRate.gamma");
+ is(event.interval, 0.5, "interval");
+
+ var e = new DeviceMotionEvent('devicemotion');
+ is(e.acceleration.x, null, "acceleration.x");
+ is(e.acceleration.y, null, "acceleration.y");
+ is(e.acceleration.z, null, "acceleration.z");
+ is(e.accelerationIncludingGravity.x, null, "accelerationIncludingGravity.x");
+ is(e.accelerationIncludingGravity.y, null, "accelerationIncludingGravity.y");
+ is(e.accelerationIncludingGravity.z, null, "accelerationIncludingGravity.z");
+ is(e.rotationRate.alpha, null, "rotationRate.alpha");
+ is(e.rotationRate.beta, null, "rotationRate.beta");
+ is(e.rotationRate.gamma, null, "rotationRate.gamma");
+ is(e.interval, null, "interval");
+
+ e = new DeviceMotionEvent('devicemotion', {
+ bubbles: true, cancelable: true,
+ acceleration: null, accelerationIncludingGravity: null,
+ rotationRate: null, interval: null
+ });
+ is(e.acceleration.x, null, "acceleration.x");
+ is(e.acceleration.y, null, "acceleration.y");
+ is(e.acceleration.z, null, "acceleration.z");
+ is(e.accelerationIncludingGravity.x, null, "accelerationIncludingGravity.x");
+ is(e.accelerationIncludingGravity.y, null, "accelerationIncludingGravity.y");
+ is(e.accelerationIncludingGravity.z, null, "accelerationIncludingGravity.z");
+ is(e.rotationRate.alpha, null, "rotationRate.alpha");
+ is(e.rotationRate.beta, null, "rotationRate.beta");
+ is(e.rotationRate.gamma, null, "rotationRate.gamma");
+ is(e.interval, null, "interval");
+
+ e = new DeviceMotionEvent('devicemotion', {
+ bubbles: true, cancelable: true,
+ acceleration: {}, accelerationIncludingGravity: {},
+ rotationRate: {}, interval: 0
+ });
+ is(e.acceleration.x, null, "acceleration.x");
+ is(e.acceleration.y, null, "acceleration.y");
+ is(e.acceleration.z, null, "acceleration.z");
+ is(e.accelerationIncludingGravity.x, null, "accelerationIncludingGravity.x");
+ is(e.accelerationIncludingGravity.y, null, "accelerationIncludingGravity.y");
+ is(e.accelerationIncludingGravity.z, null, "accelerationIncludingGravity.z");
+ is(e.rotationRate.alpha, null, "rotationRate.alpha");
+ is(e.rotationRate.beta, null, "rotationRate.beta");
+ is(e.rotationRate.gamma, null, "rotationRate.gamma");
+ is(e.interval, 0, "interval");
+
+ SimpleTest.finish();
+};
+
+window.addEventListener("devicemotion", checkMotion, true);
+
+var event = DeviceMotionEvent;
+ok(!!event, "Should have seen DeviceMotionEvent!");
+
+event = document.createEvent("DeviceMotionEvent");
+event.initDeviceMotionEvent('devicemotion', true, true,
+ {x:1.5,y:2.5,z:3.5},
+ {x:4.5,y:5.5,z:6.5},
+ {alpha:7.5,beta:8.5,gamma:9.5},
+ 0.5);
+window.dispatchEvent(event);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug667612.html b/dom/events/test/test_bug667612.html
new file mode 100644
index 0000000000..95e7f430de
--- /dev/null
+++ b/dom/events/test/test_bug667612.html
@@ -0,0 +1,38 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=667612
+-->
+<head>
+ <title>Test for Bug 667612</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=667612">Mozilla Bug 667612</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+xhr = new XMLHttpRequest;
+w = new Worker("empty.js");
+window.addEventListener("load", null);
+document.addEventListener("load", null);
+document.body.addEventListener("load", null);
+xhr.addEventListener("load", null);
+w.addEventListener("load", null);
+window.addEventListener("load", undefined);
+document.addEventListener("load", undefined);
+document.body.addEventListener("load", undefined);
+xhr.addEventListener("load", undefined);
+w.addEventListener("load", undefined);
+
+ok(true, "didn't throw");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug667919-1.html b/dom/events/test/test_bug667919-1.html
new file mode 100644
index 0000000000..d4be92dafb
--- /dev/null
+++ b/dom/events/test/test_bug667919-1.html
@@ -0,0 +1,41 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=615597
+-->
+<head>
+ <title>Test for Bug 615597</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=615597">Mozilla Bug 615597</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 615597 **/
+
+window.ondeviceorientation = function(event) {
+ is(event.alpha, 1.5);
+ is(event.beta, 2.25);
+ is(event.gamma, 3.667);
+ is(event.absolute, true);
+ SimpleTest.finish();
+};
+
+var event = DeviceOrientationEvent;
+ok(!!event, "Should have seen DeviceOrientationEvent!");
+
+event = document.createEvent("DeviceOrientationEvent");
+event.initDeviceOrientationEvent('deviceorientation', true, true, 1.5, 2.25, 3.667, true);
+window.dispatchEvent(event);
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug679494.xhtml b/dom/events/test/test_bug679494.xhtml
new file mode 100644
index 0000000000..94f8177549
--- /dev/null
+++ b/dom/events/test/test_bug679494.xhtml
@@ -0,0 +1,36 @@
+<?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=679494
+-->
+<window title="Mozilla Bug 679494" onload="doTest();"
+ 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">
+ <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=679494">Mozilla Bug 679494</a>
+ <p id="display"></p>
+<div id="content" style="display: none">
+ <iframe id="contentframe" src="http://mochi.test:8888/tests/dom/events/test/file_bug679494.html"></iframe>
+</div>
+</body>
+
+<script class="testbody" type="application/javascript"><![CDATA[
+
+/* Test for bug 679494 */
+function doTest() {
+ SimpleTest.waitForExplicitFinish();
+
+ var w = document.getElementById("contentframe").contentWindow;
+ w.addEventListener("message", function(e) {
+ is("test", e.data, "We got the data without a compartment mismatch assertion!");
+ SimpleTest.finish();
+ });
+ w.postMessage("test", "*");
+}
+
+]]></script>
+
+</window>
diff --git a/dom/events/test/test_bug684208.html b/dom/events/test/test_bug684208.html
new file mode 100644
index 0000000000..7405c9cb5c
--- /dev/null
+++ b/dom/events/test/test_bug684208.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=684208
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 684208</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 684208 **/
+
+ function checkDispatchReturnValue(targetOrUndefined) {
+ var target = targetOrUndefined ? targetOrUndefined : self;
+ function createEvent() {
+ if ("MouseEvent" in this) {
+ return new MouseEvent("click", {cancelable: true});
+ }
+ return new Event("dummy", {cancelable: true});
+ }
+
+ function postSelfMessage(msg) {
+ try {
+ self.postMessage(msg);
+ } catch(ex) {
+ self.postMessage(msg, "*");
+ }
+ }
+
+ function passiveListener(e) {
+ e.target.removeEventListener(e.type, passiveListener);
+ }
+ var event = createEvent();
+ target.addEventListener(event.type, passiveListener);
+ postSelfMessage(target.dispatchEvent(event) == true);
+
+ function cancellingListener(e) {
+ e.target.removeEventListener(e.type, cancellingListener);
+ e.preventDefault();
+ }
+ event = createEvent();
+ target.addEventListener(event.type, cancellingListener);
+ postSelfMessage(target.dispatchEvent(event) == false);
+ }
+
+ function test() {
+ var expectedEvents = 6;
+ function messageHandler(e) {
+ ok(e.data, "All the dispatchEvent calls should pass.");
+ --expectedEvents;
+ if (!expectedEvents) {
+ window.onmessage = null;
+ window.worker.onmessage = null;
+ SimpleTest.finish();
+ }
+ }
+ window.onmessage = messageHandler;
+ checkDispatchReturnValue();
+ checkDispatchReturnValue(document.getElementById("link"));
+ window.worker =
+ new Worker(URL.createObjectURL(new Blob(["(" + checkDispatchReturnValue.toString() + ")();"])));
+ window.worker.onmessage = messageHandler;
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ </script>
+</head>
+<body onload="test();">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=684208">Mozilla Bug 684208</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<a id="link" href="#foo">foo</a>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug687787.html b/dom/events/test/test_bug687787.html
new file mode 100644
index 0000000000..130aa011f2
--- /dev/null
+++ b/dom/events/test/test_bug687787.html
@@ -0,0 +1,610 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=687787
+-->
+<head>
+ <title>Test for Bug 687787</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <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=687787">Mozilla Bug 687787</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+var eventStack = [];
+
+function _callback(e){
+ var event = {'type' : e.type, 'target' : e.target, 'relatedTarget' : e.relatedTarget }
+ eventStack.push(event);
+}
+
+function clearEventStack(){
+ eventStack = [];
+}
+
+window.addEventListener("focus", _callback, true);
+window.addEventListener("focusin", _callback, true);
+window.addEventListener("focusout", _callback, true);
+window.addEventListener("blur", _callback, true);
+
+function CompareEventToExpected(e, expected) {
+ if (expected == null || e == null)
+ return false;
+ if (e.type == expected.type && e.target == expected.target && e.relatedTarget == expected.relatedTarget)
+ return true;
+ return false;
+}
+
+function TestEventOrderNormal() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var input3 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ input3.setAttribute('id', 'input3');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+ input3.setAttribute('type', 'text');
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.appendChild(input3);
+ content.style.display = 'block'
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focus',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ {'type' : 'focusin',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ {'type' : 'blur',
+ 'target' : input2,
+ 'relatedTarget' : input3},
+ {'type' : 'focusout',
+ 'target' : input2,
+ 'relatedTarget' : input3},
+ {'type' : 'focus',
+ 'target' : input3,
+ 'relatedTarget' : input2},
+ {'type' : 'focusin',
+ 'target' : input3,
+ 'relatedTarget' : input2},
+ ]
+
+ input1.focus();
+ clearEventStack();
+
+ input2.focus();
+ input3.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length ; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestEventOrderNormalFiresAtRightTime() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var input3 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ input3.setAttribute('id', 'input3');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+ input3.setAttribute('type', 'text');
+
+ input1.onblur = function(e)
+ {
+ ok(document.activeElement == document.body, 'input1: not focused when blur fires')
+ }
+
+ input1.addEventListener('focusout', function(e)
+ {
+ ok(document.activeElement == document.body, 'input1: not focused when focusout fires')
+ });
+
+ input2.onfocus = function(e)
+ {
+ ok(document.activeElement == input2, 'input2: focused when focus fires')
+ }
+
+ input2.addEventListener('focusin', function(e)
+ {
+ ok(document.activeElement == input2, 'input2: focused when focusin fires')
+ });
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.style.display = 'block'
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focus',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ {'type' : 'focusin',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ ]
+
+ input1.focus();
+ clearEventStack();
+
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length ; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestFocusOutRedirectsFocus() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var input3 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ input3.setAttribute('id', 'input3');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+ input3.setAttribute('type', 'text');
+ input1.addEventListener('focusout', function () {
+ input3.focus();
+ });
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.appendChild(input3);
+ content.style.display = 'block'
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focus',
+ 'target' : input3,
+ 'relatedTarget' : null},
+ {'type' : 'focusin',
+ 'target' : input3,
+ 'relatedTarget' : null},
+ ]
+
+ input1.focus();
+ clearEventStack();
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestFocusInRedirectsFocus() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var input3 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ input3.setAttribute('id', 'input3');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+ input3.setAttribute('type', 'text');
+ input2.addEventListener('focusin', function () {
+ input3.focus();
+ });
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.appendChild(input3);
+ content.style.display = 'block'
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focus',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ {'type' : 'focusin',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ {'type' : 'blur',
+ 'target' : input2,
+ 'relatedTarget' : input3},
+ {'type' : 'focusout',
+ 'target' : input2,
+ 'relatedTarget' : input3},
+ {'type' : 'focus',
+ 'target' : input3,
+ 'relatedTarget' : input2},
+ {'type' : 'focusin',
+ 'target' : input3,
+ 'relatedTarget' : input2},
+ ]
+
+ input1.focus();
+ clearEventStack();
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestBlurRedirectsFocus() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var input3 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ input3.setAttribute('id', 'input3');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+ input3.setAttribute('type', 'text');
+ input1.onblur = function () {
+ input3.focus();
+ }
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.appendChild(input3);
+ content.style.display = 'block'
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focus',
+ 'target' : input3,
+ 'relatedTarget' : null},
+ {'type' : 'focusin',
+ 'target' : input3,
+ 'relatedTarget' : null},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ ]
+
+ input1.focus();
+ clearEventStack();
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestFocusRedirectsFocus() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var input3 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ input3.setAttribute('id', 'input3');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+ input3.setAttribute('type', 'text');
+ input2.onfocus = function () {
+ input3.focus();
+ }
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.appendChild(input3);
+ content.style.display = 'block'
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focus',
+ 'target' : input2,
+ 'relatedTarget' : input1},
+ {'type' : 'blur',
+ 'target' : input2,
+ 'relatedTarget' : input3},
+ {'type' : 'focusout',
+ 'target' : input2,
+ 'relatedTarget' : input3},
+ {'type' : 'focus',
+ 'target' : input3,
+ 'relatedTarget' : input2},
+ {'type' : 'focusin',
+ 'target' : input3,
+ 'relatedTarget' : input2},
+ ]
+
+ input1.focus();
+ clearEventStack();
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestEventOrderDifferentDocument() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var iframe1 = document.createElement('iframe');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ iframe1.setAttribute('id', 'iframe1');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+
+ content.appendChild(input1);
+ content.appendChild(iframe1);
+ iframe1.contentDocument.body.appendChild(input2);
+ content.style.display = 'block'
+
+ iframe1.contentDocument.addEventListener("focus", _callback, true);
+ iframe1.contentDocument.addEventListener("focusin", _callback, true);
+ iframe1.contentDocument.addEventListener("focusout", _callback, true);
+ iframe1.contentDocument.addEventListener("blur", _callback, true);
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : null},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : null},
+ {'type' : 'blur',
+ 'target' : document,
+ 'relatedTarget' : null},
+ {'type' : 'blur',
+ 'target' : window,
+ 'relatedTarget' : null},
+ {'type' : 'focus',
+ 'target' : iframe1.contentDocument,
+ 'relatedTarget' : null},
+ {'type' : 'focus',
+ 'target' : input2,
+ 'relatedTarget' : null},
+ {'type' : 'focusin',
+ 'target' : input2,
+ 'relatedTarget' : null},
+ ]
+
+ input1.focus();
+ clearEventStack();
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+
+function TestFocusOutMovesTarget() {
+
+ var input1 = document.createElement('input');
+ var input2 = document.createElement('input');
+ var iframe1 = document.createElement('iframe');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input2.setAttribute('id', 'input2');
+ iframe1.setAttribute('id', 'iframe1');
+ input1.setAttribute('type', 'text');
+ input2.setAttribute('type', 'text');
+
+ input1.addEventListener('focusout', function () {
+ iframe1.contentDocument.body.appendChild(input2);
+ });
+
+ content.appendChild(input1);
+ content.appendChild(input2);
+ content.appendChild(iframe1);
+ content.style.display = 'block'
+
+ iframe1.contentDocument.addEventListener("focus", _callback, true);
+ iframe1.contentDocument.addEventListener("focusin", _callback, true);
+ iframe1.contentDocument.addEventListener("focusout", _callback, true);
+ iframe1.contentDocument.addEventListener("blur", _callback, true);
+
+ let expectedEventOrder = [
+ {'type' : 'blur',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ {'type' : 'focusout',
+ 'target' : input1,
+ 'relatedTarget' : input2},
+ ]
+
+ input1.focus();
+ clearEventStack();
+ input2.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+function TestBlurWindowAndRefocusInputOnlyFiresFocusInOnInput() {
+
+ var input1 = document.createElement('input');
+ var content = document.getElementById('content');
+
+ input1.setAttribute('id', 'input1');
+ input1.setAttribute('type', 'text');
+
+ content.appendChild(input1);
+
+ let expectedEventOrder = [
+ {'type' : 'focus',
+ 'target' : document,
+ 'relatedTarget' : null},
+ {'type' : 'focus',
+ 'target' : window,
+ 'relatedTarget' : null},
+ {'type' : 'focus',
+ 'target' : input1,
+ 'relatedTarget' : null},
+ {'type' : 'focusin',
+ 'target' : input1,
+ 'relatedTarget' : null},
+ ]
+
+ window.blur();
+ clearEventStack();
+ input1.focus();
+
+ for (var i = 0; i < expectedEventOrder.length || i < eventStack.length; i++) {
+ ok(CompareEventToExpected(expectedEventOrder[i], eventStack[i]), 'Normal event order is correct: Event ' + i + ': '
+ + 'Expected ('
+ + expectedEventOrder[i].type + ','
+ + (expectedEventOrder[i].target ? expectedEventOrder[i].target.id : null) + ','
+ + (expectedEventOrder[i].relatedTarget ? expectedEventOrder[i].relatedTarget.id : null) + '), '
+ + 'Actual ('
+ + eventStack[i].type + ','
+ + (eventStack[i].target ? eventStack[i].target.id : null) + ','
+ + (eventStack[i].relatedTarget ? eventStack[i].relatedTarget.id : null) + ')');
+ }
+
+ content.innerHTML = '';
+}
+
+TestEventOrderNormal();
+TestEventOrderNormalFiresAtRightTime();
+TestFocusOutRedirectsFocus();
+TestFocusInRedirectsFocus();
+TestBlurRedirectsFocus();
+TestFocusRedirectsFocus();
+TestFocusOutMovesTarget();
+TestEventOrderDifferentDocument();
+TestBlurWindowAndRefocusInputOnlyFiresFocusInOnInput();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug689564.html b/dom/events/test/test_bug689564.html
new file mode 100644
index 0000000000..7183e6169d
--- /dev/null
+++ b/dom/events/test/test_bug689564.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=689564
+-->
+<head>
+ <title>Test for Bug 689564</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=689564">Mozilla Bug 689564</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 689564 **/
+var div = document.createElement("div");
+div.setAttribute("onclick", "div");
+is(window.onclick, null, "div should not forward onclick");
+is(div.onclick.toString(), "function onclick(event) {\ndiv\n}",
+ "div should have an onclick handler");
+
+div.setAttribute("onscroll", "div");
+is(window.onscroll, null, "div should not forward onscroll");
+is(div.onscroll.toString(), "function onscroll(event) {\ndiv\n}",
+ "div should have an onscroll handler");
+
+div.setAttribute("onpopstate", "div");
+is(window.onpopstate, null, "div should not forward onpopstate");
+is("onpopstate" in div, false, "div should not have onpopstate handler");
+
+var body = document.createElement("body");
+body.setAttribute("onclick", "body");
+is(window.onclick, null, "body should not forward onclick");
+is(body.onclick.toString(), "function onclick(event) {\nbody\n}",
+ "body should have an onclick handler");
+body.setAttribute("onscroll", "body");
+is(window.onscroll.toString(), "function onscroll(event) {\nbody\n}",
+ "body should forward onscroll");
+body.setAttribute("onpopstate", "body");
+is(window.onpopstate.toString(), "function onpopstate(event) {\nbody\n}",
+ "body should forward onpopstate");
+
+var frameset = document.createElement("frameset");
+frameset.setAttribute("onclick", "frameset");
+is(window.onclick, null, "frameset should not forward onclick");
+is(frameset.onclick.toString(), "function onclick(event) {\nframeset\n}",
+ "frameset should have an onclick handler");
+frameset.setAttribute("onscroll", "frameset");
+is(window.onscroll.toString(), "function onscroll(event) {\nframeset\n}",
+ "frameset should forward onscroll");
+frameset.setAttribute("onpopstate", "frameset");
+is(window.onpopstate.toString(), "function onpopstate(event) {\nframeset\n}",
+ "frameset should forward onpopstate");
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug698929.html b/dom/events/test/test_bug698929.html
new file mode 100644
index 0000000000..d450ec8517
--- /dev/null
+++ b/dom/events/test/test_bug698929.html
@@ -0,0 +1,47 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=698929
+-->
+<head>
+ <title>Test for Bug 698929</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=698929">Mozilla Bug 698929</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 698929 **/
+
+ var e = document.createEvent("Event");
+ e.initEvent("foo", true, true);
+ var c = 0;
+ var b = 0;
+ document.addEventListener("foo",
+ function() {
+ ++c;
+ });
+ document.body.addEventListener("foo", function(event) {
+ ++b;
+ event.stopImmediatePropagation();
+ });
+ document.body.addEventListener("foo", function(event) {
+ ++b;
+ });
+ document.body.dispatchEvent(e);
+ document.documentElement.dispatchEvent(e);
+ is(c, 1, "Listener in the document should have been called once.");
+ is(b, 1, "Listener in the body should have been called once.");
+
+
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug704423.html b/dom/events/test/test_bug704423.html
new file mode 100644
index 0000000000..30b1913d07
--- /dev/null
+++ b/dom/events/test/test_bug704423.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=704423
+-->
+<head>
+ <title>Test for Bug 704423</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=704423">Mozilla Bug 704423</a>
+<p id="display"></p>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 704423 **/
+
+function doTest()
+{
+ function handler(aEvent) {
+ aEvent.preventDefault();
+ ok(aEvent.defaultPrevented,
+ "mousemove event should be cancelable");
+ }
+ window.addEventListener("mousemove", handler, true);
+ synthesizeMouseAtCenter(document.body, { type: "mousemove" });
+ window.removeEventListener("mousemove", handler, true);
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(doTest);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug741666.html b/dom/events/test/test_bug741666.html
new file mode 100644
index 0000000000..f67a3a7065
--- /dev/null
+++ b/dom/events/test/test_bug741666.html
@@ -0,0 +1,185 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=741666
+-->
+<head>
+ <title>Test for Bug 741666</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script 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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=741666">Mozilla Bug 741666</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 306008 - Touch events with a reference held should retain their touch lists **/
+
+let tests = [], testTarget, parent;
+
+let touch = {
+ id: 0,
+ point: {x: 0, y: 0},
+ radius: {x: 0, y: 0},
+ rotation: 0,
+ force: 0.5,
+ target: null
+}
+
+function nextTest() {
+ if (tests.length)
+ SimpleTest.executeSoon(tests.shift());
+}
+
+function checkEvent(aFakeEvent, aTouches) {
+ return function(aEvent, aTrusted) {
+ is(aFakeEvent.ctrlKey, aEvent.ctrlKey, "Correct ctrlKey");
+ is(aFakeEvent.altKey, aEvent.altKey, "Correct altKey");
+ is(aFakeEvent.shiftKey, aEvent.shiftKey, "Correct shiftKey");
+ is(aFakeEvent.metaKey, aEvent.metaKey, "Correct metaKey");
+ is(aEvent.isTrusted, aTrusted, "Event is trusted");
+ checkTouches(aFakeEvent[aTouches], aEvent[aTouches]);
+ }
+}
+
+function checkTouches(aTouches1, aTouches2) {
+ is(aTouches1.length, aTouches2.length, "Correct touches length");
+ for (var i = 0; i < aTouches1.length; i++) {
+ checkTouch(aTouches1[i], aTouches2[i]);
+ }
+}
+
+function checkTouch(aFakeTouch, aTouch) {
+ is(aFakeTouch.identifier, aTouch.identifier, "Touch has correct identifier");
+ is(aFakeTouch.target, aTouch.target, "Touch has correct target");
+ is(aFakeTouch.page.x, aTouch.pageX, "Touch has correct pageX");
+ is(aFakeTouch.page.y, aTouch.pageY, "Touch has correct pageY");
+ is(aFakeTouch.page.x + Math.round(window.mozInnerScreenX), aTouch.screenX, "Touch has correct screenX");
+ is(aFakeTouch.page.y + Math.round(window.mozInnerScreenY), aTouch.screenY, "Touch has correct screenY");
+ is(aFakeTouch.page.x, aTouch.clientX, "Touch has correct clientX");
+ is(aFakeTouch.page.y, aTouch.clientY, "Touch has correct clientY");
+ is(aFakeTouch.radius.x, aTouch.radiusX, "Touch has correct radiusX");
+ is(aFakeTouch.radius.y, aTouch.radiusY, "Touch has correct radiusY");
+ is(aFakeTouch.rotationAngle, aTouch.rotationAngle, "Touch has correct rotationAngle");
+ is(aFakeTouch.force, aTouch.force, "Touch has correct force");
+}
+
+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 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 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 testPreventDefault(name) {
+ let cwu = SpecialPowers.getDOMWindowUtils(window);
+ let target = document.getElementById("testTarget");
+ let bcr = target.getBoundingClientRect();
+
+ let touch1 = new testtouch({
+ page: {x: Math.round(bcr.left + bcr.width/2),
+ y: Math.round(bcr.top + bcr.height/2)},
+ target
+ });
+
+ let event = new touchEvent({
+ touches: [touch1],
+ targetTouches: [touch1],
+ changedTouches: [touch1]
+ });
+
+ // test touchstart event fires correctly
+ var checkTouchesEvent = checkEvent(event, "touches");
+ var checkTargetTouches = checkEvent(event, "targetTouches");
+
+ /* This is the heart of the test. Verify that the touch event
+ looks correct both in and outside of a setTimeout */
+ window.addEventListener("touchstart", function(firedEvent) {
+ checkTouchesEvent(firedEvent, true);
+ setTimeout(function() {
+ checkTouchesEvent(firedEvent, true);
+ checkTargetTouches(firedEvent, true);
+
+ event.touches = [];
+ event.targetTouches = [];
+ sendTouchEvent(cwu, "touchend", event, 0);
+
+ nextTest();
+ }, 0);
+ });
+ sendTouchEvent(cwu, "touchstart", event, 0);
+}
+
+async function doTest() {
+ await promiseApzFlushedRepaints();
+ await waitUntilApzStable();
+
+ tests.push(testPreventDefault);
+
+ tests.push(function() {
+ SimpleTest.finish();
+ });
+
+ nextTest();
+}
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doTest);
+
+</script>
+</pre>
+<div id="parent">
+ <span id="testTarget" style="margin-left: 200px; padding: 5px; border: 1px solid black;">testTarget</span>
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug812744.html b/dom/events/test/test_bug812744.html
new file mode 100644
index 0000000000..2cd677b930
--- /dev/null
+++ b/dom/events/test/test_bug812744.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=812744
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 812744</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=812744">Mozilla Bug 812744</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+<iframe id="f"></iframe>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 812744 **/
+SimpleTest.waitForExplicitFinish();
+
+addLoadEvent(function() {
+ var f = $("f");
+ var el = f.contentDocument.documentElement;
+ f.onload = function() {
+ el.setAttribute("onmouseleave", "(void 0)");
+ is(el.onmouseleave, null, "Should not have a function here");
+ SimpleTest.finish();
+ };
+ f.src = "http://www.example.com/"
+});
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug822898.html b/dom/events/test/test_bug822898.html
new file mode 100644
index 0000000000..588be7d824
--- /dev/null
+++ b/dom/events/test/test_bug822898.html
@@ -0,0 +1,343 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=822898
+-->
+<head>
+ <title>Test for Bug 822898</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=822898">Mozilla Bug 822898</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe id="subFrame"></iframe>
+</div>
+<pre id="test">
+<script class="testbody" type="application/javascript">
+
+/** Test for Bug 822898 - Pointer* Events **/
+
+let tests = [], testTarget, parent, iframeBody, gOnPointerPropHandled;
+
+function nextTest() {
+ if (tests.length)
+ SimpleTest.executeSoon(tests.shift());
+}
+
+function random() {
+ return Math.floor(Math.random() * 100);
+}
+
+function createTestEventValue(name) {
+
+ let detail = random();
+ let screenX = random();
+ let screenY = random();
+ let clientX = random();
+ let clientY = random();
+ let ctrlKey = !!(random() % 2);
+ let altKey = !!(random() % 2);
+ let shiftKey = !!(random() % 2);
+ let metaKey = !!(random() % 2);
+ let button = random();
+ let pointerId = random();
+
+ return function() {
+ let event = new PointerEvent("pointerdown", {
+ bubbles: true, cancelable: true, view: window,
+ detail, screenX, screenY, clientX, clientY,
+ ctrlKey, altKey, shiftKey, metaKey,
+ button, relatedTarget: null, pointerId
+ });
+
+
+ function check(ev) {
+ is(ev.detail, detail, "Correct detail");
+ is(ev.screenX, screenX, "Correct screenX");
+ is(ev.screenY, screenY, "Correct screenY");
+ is(ev.clientX, clientX, "Correct clientX");
+ is(ev.clientY, clientY, "Correct clientY");
+ is(ev.ctrlKey, ctrlKey, "Correct ctrlKey");
+ is(ev.altKey, altKey, "Correct altKey");
+ is(ev.shiftKey, shiftKey, "Correct shiftKey");
+ is(ev.metaKey, metaKey, "Correct metaKey");
+ is(ev.button, button, "Correct buttonArg");
+ is(ev.pointerId, pointerId, "Correct pointerId");
+ }
+
+ for (let target of [document, window, testTarget, parent])
+ target.addEventListener(name, check);
+
+ testTarget.dispatchEvent(event);
+
+ for (let target of [document, window, testTarget, parent])
+ target.removeEventListener(name, check);
+
+
+ nextTest();
+ }
+}
+
+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 testDefaultArg() {
+ let event = getDefaultArgEvent("pointerdown");
+
+ testTarget.addEventListener("pointerdown", function(ev) {
+ is(ev.pointerId, 0, "Correct default pointerId");
+ }, {once: true});
+ testTarget.dispatchEvent(event);
+
+ nextTest();
+}
+
+function testStopPropagation() {
+ let event = getDefaultArgEvent("pointerdown");
+
+ let unreachableListener = function () {
+ ok(false, "Event should have been stopped");
+ }
+
+ // Capturing phase
+ let captured = false;
+ parent.addEventListener("pointerdown", function() {
+ captured = true;
+ }, {capture: true, once: true}); // Capturing phase
+
+ // Bubbling phase
+ parent.addEventListener("pointerdown", unreachableListener);
+
+ testTarget.addEventListener("pointerdown", function(ev) {
+ is(captured, true, "Event should have been captured");
+ ev.stopPropagation();
+ }, {once: true});
+
+ testTarget.dispatchEvent(event);
+
+ parent.removeEventListener("pointerdown", unreachableListener);
+
+ nextTest();
+}
+
+function testPreventDefault() {
+ let event = getDefaultArgEvent("pointerdown");
+
+ parent.addEventListener("pointerdown", function(ev) {
+ is(ev.defaultPrevented, true, "preventDefault can be called");
+ nextTest();
+ }, {once: true});
+
+ testTarget.addEventListener("pointerdown", function(ev) {
+ ev.preventDefault();
+ }, {once: true});
+
+ testTarget.dispatchEvent(event);
+}
+
+function testBlockPreventDefault() {
+ let event = new PointerEvent("pointerdown", {
+ bubbles: true, cancelable: false, 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, pointerType: "pen"
+ });
+
+ parent.addEventListener("pointerdown", function(ev) {
+ is(ev.defaultPrevented, false, "aCancelableArg works");
+ nextTest();
+ }, {once: true});
+
+ testTarget.addEventListener("pointerdown", function(ev) {
+ ev.preventDefault();
+ }, {once: true});
+
+ testTarget.dispatchEvent(event);
+}
+
+function testBlockBubbling() {
+ let unreachableListener = function () {
+ ok(false, "aCanBubble doesn't work");
+ }
+
+ let event = new PointerEvent("pointerdown", {
+ bubbles: false, 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
+ });
+
+ parent.addEventListener("pointerdown", unreachableListener);
+ testTarget.dispatchEvent(event);
+ parent.removeEventListener("pointerdown", unreachableListener);
+
+ nextTest();
+}
+
+function testOnPointerProperty()
+{
+ iframeBody.onpointerdown = function (e) { gOnPointerPropHandled.pointerdown = true; }
+ iframeBody.onpointerup = function (e) { gOnPointerPropHandled.pointerup = true; }
+ iframeBody.onpointermove = function (e) { gOnPointerPropHandled.pointermove = true; }
+ iframeBody.onpointerout = function (e) { gOnPointerPropHandled.pointerout = true; }
+ iframeBody.onpointerover = function (e) { gOnPointerPropHandled.pointerover = true; }
+ iframeBody.onpointerenter = function (e) { gOnPointerPropHandled.pointerenter = true; }
+ iframeBody.onpointerleave = function (e) { gOnPointerPropHandled.pointerleave = true; }
+ iframeBody.onpointercancel = function (e) { gOnPointerPropHandled.pointercancel = true; }
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointerdown"));
+ is(gOnPointerPropHandled.pointerdown, true, "pointerdown property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointerup"));
+ is(gOnPointerPropHandled.pointerup, true, "pointerup property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointermove"));
+ is(gOnPointerPropHandled.pointermove, true, "pointermove property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointerout"));
+ is(gOnPointerPropHandled.pointerout, true, "pointerout property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointerover"));
+ is(gOnPointerPropHandled.pointerover, true, "pointerover property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointerenter"));
+ is(gOnPointerPropHandled.pointerenter, true, "pointerenter property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointerleave"));
+ is(gOnPointerPropHandled.pointerleave, true, "pointerleave property is performed");
+
+ iframeBody.dispatchEvent(getDefaultArgEvent("pointercancel"));
+ is(gOnPointerPropHandled.pointercancel, true, "pointercancel property is performed");
+
+ nextTest();
+}
+
+function testPointerEventCTORS()
+{
+ // TODO: This should go to test_eventctors.html, when PointerEvents enabled by default
+ var receivedEvent;
+ iframeBody.addEventListener("hello", function(e) { receivedEvent = e; }, true);
+
+ var e;
+ var ex = false;
+
+ try {
+ e = new PointerEvent();
+ } catch(exp) {
+ ex = true;
+ }
+ ok(ex, "PointerEvent: First parameter is required!");
+ ex = false;
+
+ e = new PointerEvent("hello");
+ is(e.type, "hello", "PointerEvent: Wrong event type!");
+ ok(!e.isTrusted, "PointerEvent: Event shouldn't be trusted!");
+ ok(!e.bubbles, "PointerEvent: Event shouldn't bubble!");
+ ok(!e.cancelable, "PointerEvent: Event shouldn't be cancelable!");
+ iframeBody.dispatchEvent(e);
+ is(receivedEvent, e, "PointerEvent: Wrong event!");
+
+ var PointerEventProps =
+ [ { screenX: 0 },
+ { screenY: 0 },
+ { clientX: 0 },
+ { clientY: 0 },
+ { ctrlKey: false },
+ { shiftKey: false },
+ { altKey: false },
+ { metaKey: false },
+ { button: 0 },
+ { buttons: 0 },
+ { relatedTarget: null },
+ { pointerId: 0 },
+ { pointerType: "" }
+ ];
+
+ var testPointerProps =
+ [
+ { screenX: 1 },
+ { screenY: 2 },
+ { clientX: 3 },
+ { clientY: 4 },
+ { ctrlKey: true },
+ { shiftKey: true },
+ { altKey: true },
+ { metaKey: true },
+ { button: 5 },
+ { buttons: 6 },
+ { relatedTarget: window },
+ { pointerId: 5 },
+ { pointerType: "mouse" }
+ ];
+
+ var defaultPointerEventValues = {};
+ for (var i = 0; i < PointerEventProps.length; ++i) {
+ for (prop in PointerEventProps[i]) {
+ ok(prop in e, "PointerEvent: PointerEvent doesn't have property " + prop + "!");
+ defaultPointerEventValues[prop] = PointerEventProps[i][prop];
+ }
+ }
+
+ while (testPointerProps.length) {
+ var p = testPointerProps.shift();
+ e = new PointerEvent("foo", p);
+ for (var def in defaultPointerEventValues) {
+ if (!(def in p)) {
+ is(e[def], defaultPointerEventValues[def],
+ "PointerEvent: Wrong default value for " + def + "!");
+ } else {
+ is(e[def], p[def], "PointerEvent: Wrong event init value for " + def + "!");
+ }
+ }
+ }
+ nextTest();
+}
+
+function runTests() {
+ testTarget = document.getElementById("testTarget");
+ parent = testTarget.parentNode;
+ gOnPointerPropHandled = new Array;
+ iframeBody = document.getElementById("subFrame").contentWindow.document.body;
+
+ tests.push(createTestEventValue("pointerdown"));
+ tests.push(createTestEventValue("pointermove"));
+ tests.push(createTestEventValue("pointerup"));
+
+ tests.push(testDefaultArg);
+ tests.push(testStopPropagation);
+
+ tests.push(testPreventDefault);
+ tests.push(testBlockPreventDefault);
+
+ tests.push(testBlockBubbling);
+ tests.push(testOnPointerProperty());
+ tests.push(testPointerEventCTORS());
+
+ tests.push(function() {
+ SimpleTest.finish();
+ });
+
+ nextTest();
+}
+
+window.onload = function() {
+ SimpleTest.executeSoon(runTests);
+}
+
+SimpleTest.waitForExplicitFinish();
+
+</script>
+</pre>
+<div id="parent">
+ <span id="testTarget" style="border: 1px solid black;">testTarget</span>
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug855741.html b/dom/events/test/test_bug855741.html
new file mode 100644
index 0000000000..227d4d0b6d
--- /dev/null
+++ b/dom/events/test/test_bug855741.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=855741
+-->
+<head>
+ <title>Test for Bug 855741</title>
+ <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+ <script src="/tests/SimpleTest/SimpleTest.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>
+<input type="text" id="testTarget" value="focus">
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 855741 **/
+function testFocusEvent(event) {
+ ok(('relatedTarget' in event), 'FocusEvent.relatedTarget exists');
+
+ if (event.construct_test == true) {
+ ok(event.relatedTarget == $("content"), 'FocusEvent.relatedTarget is ' + $("content").id);
+ }
+}
+
+function testUIEvent(event) {
+ ok((event.detail == 0),
+ 'UIEvent.detail should be 0 in ' + event.target.value + ' event');
+
+ ok((event.defaultView == null),
+ 'UIEvent.defaultView should be null in ' + event.target.value + ' event');
+}
+
+function testEventType(event, type) {
+ ok((event.type == type), 'Event.type match: ' + type);
+}
+
+function eventhandle(event) {
+ testFocusEvent(event);
+ testUIEvent(event);
+ testEventType(event, event.target.value);
+
+ if (event.target.value == 'blur') {
+ event.target.value = 'focus';
+ } else {
+ event.target.value = 'blur';
+ }
+}
+
+//
+// event handler:
+//
+$("testTarget").addEventListener("focus", eventhandle, true);
+$("testTarget").addEventListener("blur", eventhandle, true);
+
+//
+// FocusEvent structure test
+//
+$("testTarget").focus();
+$("testTarget").blur();
+
+//
+// Focus/Blur constructor test
+//
+var focus_event = new FocusEvent("focus",
+ {bubbles: true,
+ cancelable: true,
+ relatedTarget: $("content")});
+focus_event.construct_test = true;
+
+var blur_event = new FocusEvent("blur",
+ {bubbles: true,
+ cancelable: true,
+ relatedTarget: $("content")});
+blur_event.construct_test = true;
+
+// create cycle referece for leak test
+$("content").foo_focus = focus_event;
+$("content").foo_blur = blur_event;
+
+$("testTarget").dispatchEvent(focus_event);
+$("testTarget").dispatchEvent(blur_event);
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug864040.html b/dom/events/test/test_bug864040.html
new file mode 100644
index 0000000000..c4f5e8cf98
--- /dev/null
+++ b/dom/events/test/test_bug864040.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=864040
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 864040</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"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=864040">Mozilla Bug 864040</a>
+<div id="display">
+ <textarea id="ta" rows="5" cols="20" style="-moz-appearance:none"></textarea>
+ <div id="ce" contentEditable="true" style="height: 5em;"></div>
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ /**
+ * Test for Bug 864040
+ *
+ * We use a selection event to set the selection to the end of an editor
+ * containing an ending newline. Then we test to see that the caret is
+ * actually drawn on the newline.
+ */
+
+ async function testSelectEndOfText(elem) {
+ var tn = elem.tagName;
+ elem.focus();
+
+ // Enter test string into editor
+ var test_string = 'test\n';
+ sendString(test_string);
+
+ // Get the caret position after what we entered
+ var result = synthesizeQuerySelectedText();
+ ok(result, tn + ': failed to query selection (1)');
+ var refoffset = result.offset;
+
+ // Take a snapshot of where the caret is (on the new line)
+ referenceSnapshot = await snapshotWindow(window, true /* withCaret */);
+ ok(referenceSnapshot, tn + ': failed to take snapshot (1)');
+
+ // Set selection to the same spot through a selection event
+ ok(await synthesizeSelectionSet(refoffset, 0, false), tn + ': failed to set selection');
+
+ // Make sure new selection is the same
+ result = synthesizeQuerySelectedText();
+ ok(result, tn + ': failed to query selection (2)');
+ is(result.offset, refoffset, tn + ': caret is not at the right position');
+
+ // Take a snapshot of where the new caret is (shoud still be on the new line)
+ testSnapshot = await snapshotWindow(window, true /* withCaret */);
+ ok(testSnapshot, tn + ': failed to take snapshot (2)');
+
+ // Compare snapshot (should be the same)
+ result = compareSnapshots(referenceSnapshot, testSnapshot, true /* expected */)
+ ok(result, tn + ': failed to compare snapshots');
+ // result = [correct, s1data, s2data]
+ ok(result[0], tn + ': caret is not on new line');
+ if (!result[0]) {
+ dump('Ref: ' + result[1] + '\n');
+ dump('Res: ' + result[2] + '\n');
+ }
+ }
+
+ async function runTests() {
+ // we don't test regular <input> because this test is about multiline support
+ // test textarea
+ await testSelectEndOfText(document.getElementById('ta'));
+ // test contentEditable
+ await testSelectEndOfText(document.getElementById('ce'));
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.waitForFocus(runTests);
+ </script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug924087.html b/dom/events/test/test_bug924087.html
new file mode 100644
index 0000000000..58eecc99c4
--- /dev/null
+++ b/dom/events/test/test_bug924087.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=924087
+-->
+<head>
+ <title>Test for Bug 924087</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>
+<div contenteditable><a id="editable" href="#">editable link</a></div>
+<a id="noneditable" href="#">non-editable link</a>
+<input>
+<textarea></textarea>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 924087 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ var editable = document.querySelector("#editable");
+ var noneditable = document.querySelector("#noneditable");
+ var input = document.querySelector("input");
+ var textarea = document.querySelector("textarea");
+ synthesizeMouseAtCenter(noneditable, {type:"mousedown"});
+ is(document.querySelector(":active:link"), noneditable, "Normal links should become :active");
+ synthesizeMouseAtCenter(noneditable, {type:"mouseup"});
+ synthesizeMouseAtCenter(editable, {type:"mousedown"});
+ is(document.querySelector(":active:link"), null, "Editable links should not become :active");
+ synthesizeMouseAtCenter(editable, {type:"mouseup"});
+ [input, textarea].forEach(textbox => {
+ synthesizeMouseAtCenter(textbox, {type:"mousedown"});
+ is(document.querySelector(textbox.localName + ":active"), textbox, "The textbox should become :active");
+ synthesizeMouseAtCenter(textbox, {type:"mouseup"});
+ });
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+
+</body>
+</html>
diff --git a/dom/events/test/test_bug930374-chrome.html b/dom/events/test/test_bug930374-chrome.html
new file mode 100644
index 0000000000..576844c23f
--- /dev/null
+++ b/dom/events/test/test_bug930374-chrome.html
@@ -0,0 +1,57 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=930374
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 930374</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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=930374">Mozilla Bug 930374</a>
+<div id="display">
+ <input id="input-text">
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var gKeyPress = null;
+ function onKeyPress(aEvent)
+ {
+ gKeyPress = aEvent;
+ is(aEvent.target, document.getElementById("input-text"), "input element should have focus");
+ ok(!aEvent.defaultPrevented, "keypress event should be consumed before keypress event handler");
+ }
+
+ function runTests()
+ {
+ document.addEventListener("keypress", onKeyPress);
+ var input = document.getElementById("input-text");
+ input.focus();
+
+ input.addEventListener("input", function (aEvent) {
+ ok(gKeyPress,
+ "Test1: keypress event must be fired before an input event");
+ ok(gKeyPress.defaultPrevented,
+ "Test1: keypress event's defaultPrevented should be true in chrome even if it's consumed by default action handler of editor");
+ setTimeout(function () {
+ ok(gKeyPress.defaultPrevented,
+ "Test2: keypress event's defaultPrevented should be true after event dispatching finished");
+ SimpleTest.finish();
+ }, 0);
+ }, {once: true});
+
+ sendChar("a");
+ }
+
+ SimpleTest.waitForFocus(runTests);
+ </script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug930374-content.html b/dom/events/test/test_bug930374-content.html
new file mode 100644
index 0000000000..bcf2eadfb7
--- /dev/null
+++ b/dom/events/test/test_bug930374-content.html
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=930374
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 930374</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=930374">Mozilla Bug 930374</a>
+<div id="display">
+ <input id="input-text">
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+ <script type="application/javascript">
+ SimpleTest.waitForExplicitFinish();
+
+ var gKeyPress = null;
+ function onKeyPress(aEvent)
+ {
+ gKeyPress = aEvent;
+ is(aEvent.target, document.getElementById("input-text"), "input element should have focus");
+ ok(!aEvent.defaultPrevented, "keypress event should be consumed before keypress event handler");
+ }
+
+ function runTests()
+ {
+ document.addEventListener("keypress", onKeyPress);
+ var input = document.getElementById("input-text");
+ input.focus();
+
+ input.addEventListener("input", function (aEvent) {
+ ok(gKeyPress,
+ "Test1: keypress event must be fired before an input event");
+ ok(!gKeyPress.defaultPrevented,
+ "Test1: keypress event's defaultPrevented should be false even though it's consumed by the default action handler of editor");
+ gKeyPress.preventDefault();
+ ok(gKeyPress.defaultPrevented,
+ "Test1: keypress event's defaultPrevented should become true because of a call of preventDefault()");
+ }, {once: true});
+
+ sendChar("a");
+ gKeyPress = null;
+
+ input.addEventListener("input", function (aEvent) {
+ ok(gKeyPress,
+ "Test2: keypress event must be fired before an input event");
+ ok(!gKeyPress.defaultPrevented,
+ "Test2: keypress event's defaultPrevented should be false even though it's consumed by the default action handler of editor");
+ setTimeout(function () {
+ ok(!gKeyPress.defaultPrevented,
+ "Test2: keypress event's defaultPrevented should not become true after event dispatching finished");
+ SimpleTest.finish();
+ }, 0);
+ }, {once: true});
+
+ sendChar("b");
+ }
+
+ SimpleTest.waitForFocus(runTests);
+ </script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug944011.html b/dom/events/test/test_bug944011.html
new file mode 100644
index 0000000000..a8a0720989
--- /dev/null
+++ b/dom/events/test/test_bug944011.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=944011
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 944011</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 944011 comment 24 - Event handlers should fire even if the
+ target comes from a non-current inner. **/
+ SimpleTest.waitForExplicitFinish();
+ var gLoadCount = 0;
+ function loaded() {
+ ++gLoadCount;
+ switch(gLoadCount) {
+ case 1:
+ ok(true, "Got first load");
+ oldBody = window[0].document.body;
+ oldBody.onclick = function() {
+ ok(true, "Got onclick");
+ SimpleTest.finish();
+ }
+ $('ifr').setAttribute('srcdoc', '<html><body>Second frame</body></html>');
+ break;
+ case 2:
+ ok(true, "Got second load");
+ oldBody.dispatchEvent(new MouseEvent('click'));
+ break;
+ default:
+ ok(false, "Unexpected load");
+ SimpleTest.finish();
+ }
+ }
+
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=944011">Mozilla Bug 944011</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+ <iframe id="ifr" onload="loaded();" srcdoc="<html><body>foo</body></html>"></iframe>
+ <div name="testTarget"></div>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug944847.html b/dom/events/test/test_bug944847.html
new file mode 100644
index 0000000000..199c561d26
--- /dev/null
+++ b/dom/events/test/test_bug944847.html
@@ -0,0 +1,42 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=944847
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 944847</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 944847 **/
+
+ var e1 = document.createElement("div");
+ is(e1.onclick, null);
+ e1.setAttribute("onclick", "");
+ isnot(e1.onclick, null);
+
+ var e2 = document.implementation.createHTMLDocument(null, null).createElement("div");
+ is(e2.onclick, null);
+ e2.setAttribute("onclick", "");
+ is(e2.onclick, null);
+
+ var e3 = document.createElement("div");
+ is(e3.onclick, null);
+ e3.setAttribute("onclick", "");
+ e2.ownerDocument.adoptNode(e3);
+ is(e3.onclick, null);
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=944847">Mozilla Bug 944847</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug946632.html b/dom/events/test/test_bug946632.html
new file mode 100644
index 0000000000..5ac44141c3
--- /dev/null
+++ b/dom/events/test/test_bug946632.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=946632
+-->
+<head>
+ <title>Test for bug 946632 - propagate mouse-wheel vertical scroll events to container</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script type="application/javascript" src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ .scrollable {
+ overflow: scroll;
+ height: 200px;
+ width: 200px;
+ }
+ input {
+ font-size: 72px;
+ height: 20px;
+ width: 20px;
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=946632">Mozilla Bug 946632</a>
+<p id="display"></p>
+<div id="container" class="scrollable">
+ <input value="value">
+ x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>x<br>
+ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+ SpecialPowers.pushPrefEnv({
+ "set":[["general.smoothScroll", false],
+ ["mousewheel.system_scroll_override.enabled", false],
+ ["test.events.async.enabled", true]]
+ }, runTests)
+ }, window);
+
+var input = document.querySelector("input");
+var container = document.querySelector("#container");
+
+function reset()
+{
+ container.scrollTop = 0;
+ container.scrollLeft = 0;
+ input.scrollTop = 0;
+ input.scrollLeft = 0;
+ container.style.display='none';
+ container.getBoundingClientRect();
+}
+
+function prepare(check)
+{
+ return new Promise(resolve => {
+ container.style.display='';
+ container.getBoundingClientRect();
+ scrollHandler = function(event) {
+ window.removeEventListener("scroll", arguments.callee, true);
+ event.stopPropagation();
+ check(event)
+ resolve();
+ };
+ window.addEventListener("scroll", scrollHandler, true);
+ });
+}
+
+var tests = [
+ {
+ check(event) {
+ is(event.target, container, "<input> vertical line scroll targets container");
+ ok(container.scrollTop > 0, "<input> vertical line scroll container.scrollTop");
+ is(container.scrollLeft, 0, "<input> vertical line scroll container.scrollLeft");
+ is(input.scrollTop, 0, "<input> horizontal line scroll input.scrollTop");
+ is(input.scrollLeft, 0, "<input> horizontal line scroll input.scrollLeft");
+ },
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0,
+ lineOrPageDeltaY: 1,
+ }
+ },
+ {
+ check(event) {
+ is(event.target, input, "<input> horizontal line scroll targets <input>");
+ is(input.scrollTop, 0, "<input> horizontal line scroll input.scrollTop");
+ ok(input.scrollLeft > 0, "<input> horizontal line scroll input.scrollLeft");
+ is(container.scrollTop, 0, "<input> horizontal line scroll container.scrollTop");
+ is(container.scrollLeft, 0, "<input> horizontal line scroll container.scrollLeft");
+ },
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0,
+ lineOrPageDeltaX: 1
+ }
+ },
+ {
+ check(event) {
+ is(event.target, container, "<input> vertical page scroll targets container");
+ ok(container.scrollTop > 0, "<input> vertical line scroll container.scrollTop");
+ is(container.scrollLeft, 0, "<input> vertical line scroll container.scrollLeft");
+ is(input.scrollTop, 0, "<input> vertical page scroll input.scrollTop");
+ is(input.scrollLeft, 0, "<input> vertical page scroll input.scrollLeft");
+ },
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0,
+ lineOrPageDeltaY: 1
+ }
+ },
+ {
+ check(event) {
+ is(event.target, input, "<input> horizontal page scroll targets <input>");
+ is(input.scrollTop, 0, "<input> horizontal page scroll input.scrollTop");
+ ok(input.scrollLeft > 0, "<input> horizontal page scroll input.scrollLeft");
+ is(container.scrollTop, 0, "<input> horizontal page scroll container.scrollTop");
+ is(container.scrollLeft, 0, "<input> horizontal page scroll container.scrollLeft");
+ },
+ event: {
+ deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0,
+ lineOrPageDeltaX: 1
+ }
+ },
+];
+
+async function runTests()
+{
+ for (var i = 0; i < tests.length; i++) {
+ var test = tests[i];
+ reset();
+ await promiseApzFlushedRepaints();
+ let testEndPromise = prepare(test.check);
+ await new Promise(resolve => {
+ sendWheelAndPaint(input, 8, 6, test.event, resolve);
+ });
+ await testEndPromise;
+ }
+
+ SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug967796.html b/dom/events/test/test_bug967796.html
new file mode 100644
index 0000000000..982b9e0980
--- /dev/null
+++ b/dom/events/test/test_bug967796.html
@@ -0,0 +1,236 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=967796
+-->
+<head>
+ <title>Test for Bug 967796</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=967796">Mozilla Bug 967796</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 967796 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+var outer;
+var middle;
+var inner;
+var outside;
+var container;
+var file;
+var iframe;
+var checkRelatedTarget = false;
+var expectedRelatedEnter = null;
+var expectedRelatedLeave = null;
+var pointerentercount = 0;
+var pointerleavecount = 0;
+var pointerovercount = 0;
+var pointeroutcount = 0;
+
+function sendMouseEventToElement(t, elem) {
+ var r = elem.getBoundingClientRect();
+ synthesizeMouse(elem, r.width / 2, r.height / 2, {type: t});
+}
+
+var expectedPointerEnterTargets = [];
+var expectedPointerLeaveTargets = [];
+
+function runTests() {
+ outer = document.getElementById("outertest");
+ middle = document.getElementById("middletest");
+ inner = document.getElementById("innertest");
+ outside = document.getElementById("outside");
+ container = document.getElementById("container");
+ file = document.getElementById("file");
+ iframe = document.getElementById("iframe");
+ iframe.addEventListener("pointerenter", penter);
+ iframe.addEventListener("pointerleave", pleave);
+ iframe.addEventListener("pointerout", pout);
+ iframe.addEventListener("pointerover", pover);
+
+ // Make sure ESM thinks pointer is outside the test elements.
+ sendMouseEventToElement("mousemove", outside);
+
+ pointerentercount = 0;
+ pointerleavecount = 0;
+ pointerovercount = 0;
+ pointeroutcount = 0;
+ checkRelatedTarget = true;
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = inner;
+ expectedPointerEnterTargets = ["outertest", "middletest", "innertest"];
+ sendMouseEventToElement("mousemove", inner);
+ is(pointerentercount, 3, "Unexpected pointerenter event count!");
+ is(pointerovercount, 1, "Unexpected pointerover event count!");
+ is(pointeroutcount, 0, "Unexpected pointerout event count!");
+ is(pointerleavecount, 0, "Unexpected pointerleave event count!");
+ expectedRelatedEnter = inner;
+ expectedRelatedLeave = outside;
+ expectedPointerLeaveTargets = ["innertest", "middletest", "outertest"];
+ sendMouseEventToElement("mousemove", outside);
+ is(pointerentercount, 3, "Unexpected pointerenter event count!");
+ is(pointerovercount, 1, "Unexpected pointerover event count!");
+ is(pointeroutcount, 1, "Unexpected pointerout event count!");
+ is(pointerleavecount, 3, "Unexpected pointerleave event count!");
+
+ // Event handling over native anonymous content.
+ var r = file.getBoundingClientRect();
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = file;
+ synthesizeMouse(file, r.width / 6, r.height / 2, {type: "mousemove"});
+ is(pointerentercount, 4, "Unexpected pointerenter event count!");
+ is(pointerovercount, 2, "Unexpected pointerover event count!");
+ is(pointeroutcount, 1, "Unexpected pointerout event count!");
+ is(pointerleavecount, 3, "Unexpected pointerleave event count!");
+
+ // Moving pointer over type="file" shouldn't cause pointerover/out/enter/leave events
+ synthesizeMouse(file, r.width - (r.width / 6), r.height / 2, {type: "mousemove"});
+ is(pointerentercount, 4, "Unexpected pointerenter event count!");
+ is(pointerovercount, 2, "Unexpected pointerover event count!");
+ is(pointeroutcount, 1, "Unexpected pointerout event count!");
+ is(pointerleavecount, 3, "Unexpected pointerleave event count!");
+
+ expectedRelatedEnter = file;
+ expectedRelatedLeave = outside;
+ sendMouseEventToElement("mousemove", outside);
+ is(pointerentercount, 4, "Unexpected pointerenter event count!");
+ is(pointerovercount, 2, "Unexpected pointerover event count!");
+ is(pointeroutcount, 2, "Unexpected pointerout event count!");
+ is(pointerleavecount, 4, "Unexpected pointerleave event count!");
+
+ // Initialize iframe
+ iframe.contentDocument.documentElement.style.overflow = "hidden";
+ iframe.contentDocument.body.style.margin = "0px";
+ iframe.contentDocument.body.style.width = "100%";
+ iframe.contentDocument.body.style.height = "100%";
+ iframe.contentDocument.body.innerHTML =
+ "<div style='width: 100%; height: 50%; border: 1px solid black;'></div>" +
+ "<div style='width: 100%; height: 50%; border: 1px solid black;'></div>";
+ iframe.contentDocument.body.offsetLeft; // flush
+
+ iframe.contentDocument.body.firstChild.onpointerenter = penter;
+ iframe.contentDocument.body.firstChild.onpointerleave = pleave;
+ iframe.contentDocument.body.lastChild.onpointerenter = penter;
+ iframe.contentDocument.body.lastChild.onpointerleave = pleave;
+ r = iframe.getBoundingClientRect();
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = iframe;
+ // Move pointer inside the iframe.
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height / 4, {type: "mousemove"},
+ iframe.contentWindow);
+ is(pointerentercount, 6, "Unexpected pointerenter event count!");
+ is(pointerleavecount, 4, "Unexpected pointerleave event count!");
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height - (r.height / 4), {type: "mousemove"},
+ iframe.contentWindow);
+ is(pointerentercount, 7, "Unexpected pointerenter event count!");
+ is(pointerleavecount, 5, "Unexpected pointerleave event count!");
+ expectedRelatedEnter = iframe;
+ expectedRelatedLeave = outside;
+ sendMouseEventToElement("mousemove", outside);
+ is(pointerentercount, 7, "Unexpected pointerenter event count!");
+ is(pointerleavecount, 7, "Unexpected pointerleave event count!");
+
+ // pointerdown must produce pointerenter event
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = iframe;
+ // Move pointer inside the iframe.
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height / 4, {type: "mousedown"},
+ iframe.contentWindow);
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height - (r.height / 4), {type: "mousedown"},
+ iframe.contentWindow);
+ is(pointerentercount, 10, "Unexpected pointerenter event count!");
+
+ // pointerdown + pointermove must produce single pointerenter event
+ expectedRelatedEnter = outside;
+ expectedRelatedLeave = iframe;
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2, r.height / 4, {type: "mousedown"},
+ iframe.contentWindow);
+ synthesizeMouse(iframe.contentDocument.body, r.width / 2 + 1, r.height / 4 + 1, {type: "mousemove"},
+ iframe.contentWindow);
+ is(pointerentercount, 11, "Unexpected pointerenter event count!");
+
+ Array.from(document.querySelectorAll('*'))
+ .concat([iframe.contentDocument.body.firstChild, iframe.contentDocument.body.lastChild])
+ .forEach((elt) => {
+ elt.onpointerenter = null;
+ elt.onpointerleave = null;
+ elt.onpointerenter = null;
+ elt.onpointerleave = null;
+ });
+ SimpleTest.finish();
+}
+
+function penter(evt) {
+ ++pointerentercount;
+ evt.stopPropagation();
+ if (expectedPointerEnterTargets.length) {
+ var t = expectedPointerEnterTargets.shift();
+ is(evt.target.id, t, "Wrong event target!");
+ }
+ is(evt.bubbles, false, evt.type + " should not bubble!");
+ is(evt.cancelable, false, evt.type + " is cancelable!");
+ is(evt.target, evt.currentTarget, "Wrong event target!");
+ ok(!evt.relatedTarget || evt.target.ownerDocument == evt.relatedTarget.ownerDocument,
+ "Leaking nodes to another document?");
+ if (checkRelatedTarget && evt.target.ownerDocument == document) {
+ is(evt.relatedTarget, expectedRelatedEnter, "Wrong related target (pointerenter)");
+ }
+}
+
+function pleave(evt) {
+ ++pointerleavecount;
+ evt.stopPropagation();
+ if (expectedPointerLeaveTargets.length) {
+ var t = expectedPointerLeaveTargets.shift();
+ is(evt.target.id, t, "Wrong event target!");
+ }
+ is(evt.bubbles, false, evt.type + " should not bubble!");
+ is(evt.cancelable, false, evt.type + " is cancelable!");
+ is(evt.target, evt.currentTarget, "Wrong event target!");
+ ok(!evt.relatedTarget || evt.target.ownerDocument == evt.relatedTarget.ownerDocument,
+ "Leaking nodes to another document?");
+ if (checkRelatedTarget && evt.target.ownerDocument == document) {
+ is(evt.relatedTarget, expectedRelatedLeave, "Wrong related target (pointerleave)");
+ }
+}
+
+function pover(evt) {
+ ++pointerovercount;
+ evt.stopPropagation();
+}
+
+function pout(evt) {
+ ++pointeroutcount;
+ evt.stopPropagation();
+}
+
+</script>
+</pre>
+<div id="container" onpointerenter="penter(event)" onpointerleave="pleave(event)"
+ onpointerout="pout(event)" onpointerover="pover(event)">
+ <div id="outside" onpointerout="event.stopPropagation()" onpointerover="event.stopPropagation()">foo</div>
+ <div id="outertest" onpointerenter="penter(event)" onpointerleave="pleave(event)"
+ onpointerout="pout(event)" onpointerover="pover(event)">
+ <div id="middletest" onpointerenter="penter(event)" onpointerleave="pleave(event)"
+ onpointerout="pout(event)" onpointerover="pover(event)">
+ <div id="innertest" onpointerenter="penter(event)" onpointerleave="pleave(event)"
+ onpointerout="pout(event)" onpointerover="pover(event)">foo</div>
+ </div>
+ </div>
+ <input type="file" id="file"
+ onpointerenter="penter(event)" onpointerleave="pleave(event)"
+ onpointerout="pout(event)" onpointerover="pover(event)">
+ <br>
+ <iframe id="iframe" width="50" height="50"></iframe>
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_bug985988.html b/dom/events/test/test_bug985988.html
new file mode 100644
index 0000000000..51d02d828f
--- /dev/null
+++ b/dom/events/test/test_bug985988.html
@@ -0,0 +1,89 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=985988
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 985988</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 985988 **/
+
+ function handler() {
+ return false;
+ }
+
+ function reversedHandler() {
+ return true;
+ }
+
+ function test() {
+ var t = document.getElementById("testtarget");
+
+ t.onclick = handler;
+ var e = new MouseEvent("click", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(e.defaultPrevented, "Should have prevented default handling.");
+
+ t.onclick = reversedHandler;
+ e = new MouseEvent("click", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(!e.defaultPrevented, "Shouldn't have prevented default handling.");
+
+ t.onmouseover = handler;
+ e = new MouseEvent("mouseover", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(e.defaultPrevented, "Should have prevented default handling.");
+
+ t.onmouseover = reversedHandler;
+ e = new MouseEvent("mouseover", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(!e.defaultPrevented, "Shouldn't have prevented default handling.");
+
+ // error does not have reversed meaning for handler return value on
+ // non-globals.
+ t.onerror = handler;
+ e = new ErrorEvent("error", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(e.defaultPrevented, "Should have prevented default handling.");
+
+ t.onerror = reversedHandler;
+ e = new ErrorEvent("error", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(!e.defaultPrevented, "Shouldn't have prevented default handling.");
+
+ // error has reversed meaning for handler return value on globals.
+ t = document.getElementById("testtarget2").contentWindow;
+ t.onerror = reversedHandler;
+ e = new ErrorEvent("error", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(e.defaultPrevented, "Should have prevented default handling.");
+
+ t.onerror = handler;
+ e = new ErrorEvent("error", {cancelable: true});
+ t.dispatchEvent(e);
+ ok(!e.defaultPrevented, "Shouldn't have prevented default handling.");
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(test);
+
+ </script>
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=985988">Mozilla Bug 985988</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<a href="#" id="testtarget">test target</a>
+<iframe id="testtarget2"></iframe>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_bug998809.html b/dom/events/test/test_bug998809.html
new file mode 100644
index 0000000000..0fc50ec547
--- /dev/null
+++ b/dom/events/test/test_bug998809.html
@@ -0,0 +1,35 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=998809
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 998809</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 998809 **/
+ var event1 = document.createEvent("Event");
+ event1.initEvent("a", false, false);
+ event1.initEvent("b", false, false);
+ is(event1.type, "b");
+ var event2 = document.createEvent("Event");
+ event2.initEvent("a", false, false);
+ is(event2.type, "a");
+ event2.initEvent("b", false, false);
+ is(event2.type, "b");
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=998809">Mozilla Bug 998809</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_click_on_reframed_generated_text.html b/dom/events/test/test_click_on_reframed_generated_text.html
new file mode 100644
index 0000000000..e8c8b092d6
--- /dev/null
+++ b/dom/events/test/test_click_on_reframed_generated_text.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Test for bug 1497524: Unbound generated content in the active chain</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#target::before {
+ content: "X";
+ color: green;
+}
+</style>
+Should get a click event when clicking on the X below.
+<div id="target"></div>
+<script>
+SimpleTest.waitForExplicitFinish();
+let target = document.getElementById("target");
+
+target.addEventListener("mousedown", () => target.style.display = "inline");
+target.addEventListener("mouseup", () => target.style.display = "block");
+target.addEventListener("click", () => {
+ ok(true, "Got click event");
+ SimpleTest.finish();
+});
+
+onload = function() {
+ requestAnimationFrame(() => {
+ synthesizeMouseAtCenter(target, { type: "mousedown" })
+ requestAnimationFrame(() => {
+ synthesizeMouseAtCenter(target, { type: "mouseup" })
+ });
+ });
+}
+</script>
diff --git a/dom/events/test/test_click_on_restyled_element.html b/dom/events/test/test_click_on_restyled_element.html
new file mode 100644
index 0000000000..a79789ce74
--- /dev/null
+++ b/dom/events/test/test_click_on_restyled_element.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for clicking on an element which is restyled/reframed by mousedown event</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>
+ .before-pseudo-element *:active::before {
+ content: "";
+ display: block;
+ height: 2px;
+ position: absolute;
+ top: -2px;
+ left: 0;
+ width: 100%;
+ }
+ .position-relative *:active {
+ position: relative;
+ top: 1px;
+ }
+ </style>
+</head>
+<body>
+<section class="before-pseudo-element"><a href="about:blank">link</a></section><!-- bug 1398196 -->
+<section class="before-pseudo-element"><span>span</span></section>
+<section class="position-relative"><a href="about:blank">link</a></section><!-- bug 1506508 -->
+<section class="position-relative"><span>span</span></section>
+<script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function doTest() {
+ for (let sectionId of ["before-pseudo-element", "position-relative"]) {
+ for (let element of ["a", "span"]) {
+ let target = document.querySelector(`section.${sectionId} ${element}`);
+ target.scrollIntoView(true);
+ let clicked = false;
+ target.addEventListener("click", (aEvent) => {
+ is(aEvent.target, target, `click event is fired on the <${element}> element in ${sectionId} section as expected`);
+ aEvent.preventDefault();
+ clicked = true;
+ }, {once: true});
+ synthesizeMouseAtCenter(target, {});
+ ok(clicked, `Click event should've been fired on the <${element}> element in ${sectionId} section`);
+ }
+ }
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_clickevent_on_input.html b/dom/events/test/test_clickevent_on_input.html
new file mode 100644
index 0000000000..6f180d447b
--- /dev/null
+++ b/dom/events/test/test_clickevent_on_input.html
@@ -0,0 +1,108 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test click event on input</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>
+<p id="display">
+<input id="input"
+ style="position: absolute; top: 5px; left: 5px; border: solid 15px blue; width: 100px; height: 20px;"
+ onclick="gClickCount++;">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var gClickCount = 0;
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+var input = document.getElementById("input");
+
+function runTests()
+{
+ for (var i = 0; i < 3; i++) {
+ doTest(i);
+ }
+
+ // Re-test left clicking when the input element has some text.
+ gClickCount = 0;
+ input.value = "Long text Long text Long text Long text Long text Long text";
+ doTest(0);
+
+ input.style.display = "none";
+ SimpleTest.finish();
+}
+
+function isEnabledMiddleClickPaste()
+{
+ try {
+ return SpecialPowers.getBoolPref("middlemouse.paste");
+ } catch (e) {
+ return false;
+ }
+}
+
+function isEnabledAccessibleCaret()
+{
+ try {
+ return SpecialPowers.getBoolPref("layout.accessiblecaret.enabled");
+ } catch (e) {
+ return false;
+ }
+}
+
+function doTest(aButton)
+{
+ // NOTE #1: Non-primary buttons don't generate 'click' events
+ // NOTE #2: If touch caret is enabled, touch caret would ovelap input element,
+ // then, the click event isn't generated.
+ if (aButton != 2 &&
+ aButton != 1 &&
+ (aButton != 0 || !isEnabledAccessibleCaret())) {
+ gClickCount = 0;
+ // click on border of input
+ synthesizeMouse(input, 5, 5, { button: aButton });
+ is(gClickCount, 1,
+ "click event doesn't fired on input element (button is " +
+ aButton + ")");
+
+ gClickCount = 0;
+ // down on border
+ synthesizeMouse(input, 5, 5, { type: "mousedown", button: aButton });
+ // up on anonymous div of input
+ synthesizeMouse(input, 20, 20, { type: "mouseup", button: aButton });
+ is(gClickCount, 1,
+ "click event doesn't fired on input element (button is " +
+ aButton + ")");
+
+ gClickCount = 0;
+ // down on anonymous div of input
+ synthesizeMouse(input, 20, 20, { type: "mousedown", button: aButton });
+ // up on border
+ synthesizeMouse(input, 5, 5, { type: "mouseup", button: aButton });
+ is(gClickCount, 1,
+ "click event doesn't fired on input element (button is " +
+ aButton + ")");
+ }
+
+ gClickCount = 0;
+ // down on outside of input
+ synthesizeMouse(input, -3, -3, { type: "mousedown", button: aButton });
+ // up on border
+ synthesizeMouse(input, 5, 5, { type: "mouseup", button: aButton });
+ is(gClickCount, 0,
+ "click event is fired on input element unexpectedly (button is " +
+ aButton + ")");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_coalesce_mousewheel.html b/dom/events/test/test_coalesce_mousewheel.html
new file mode 100644
index 0000000000..41af7d5b03
--- /dev/null
+++ b/dom/events/test/test_coalesce_mousewheel.html
@@ -0,0 +1,241 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>mousewheel coalescing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="apz_test_utils.js"></script>
+ <script src="apz_test_native_event_utils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ let wheelEvents = [];
+ function mousewheel(aWheelEvent) {
+ // Make mousewheel handling slow
+ var start = performance.now();
+ while (performance.now() < (start + 10));
+ wheelEvents.push(aWheelEvent);
+ }
+
+ function resolveIfProcessed(resolve, minamount) {
+ if (wheelEvents.length >= minamount) {
+ SimpleTest.requestFlakyTimeout("Make sure we got all events.");
+ setTimeout(function() { resolve(); }, 20);
+ } else {
+ setTimeout(function() { resolveIfProcessed(resolve, minamount); });
+ }
+ }
+
+ const kLineDeltaFactor =
+ SpecialPowers.getBoolPref("mousewheel.system_scroll_override.enabled", false)
+ ? SpecialPowers.getIntPref("mousewheel.system_scroll_override.vertical.factor", 200) / 100
+ : 1;
+
+ function checkWheelEvents(aExpectedDeltaMode, aExpectedSumOfDeltaY, aDescription) {
+ const lineDeltaFactor =
+ aExpectedDeltaMode === WheelEvent.DOM_DELTA_LINE ? kLineDeltaFactor : 1;
+ let succeeded = true;
+ let deltaY = 0;
+ for (const wheelEvent of wheelEvents) {
+ succeeded &= wheelEvent.deltaMode === aExpectedDeltaMode;
+ is(wheelEvent.deltaMode, aExpectedDeltaMode,
+ `When ${aDescription}, the deltaMode of all wheel events should be ${aExpectedDeltaMode}`);
+ deltaY += wheelEvent.deltaY;
+ }
+ succeeded &= deltaY == aExpectedSumOfDeltaY * lineDeltaFactor;
+ is(deltaY, aExpectedSumOfDeltaY * lineDeltaFactor,
+ `When ${aDescription}, sum of the deltaY of all wheel events should be ${aExpectedSumOfDeltaY * lineDeltaFactor}`);
+ return succeeded;
+ }
+
+ async function testOneSingleWheelEvent() {
+ await new Promise(function(resolve) {
+ wheelEvents = [];
+ var element = document.getElementById("wheelEventReceiver");
+ element.addEventListener("wheel", mousewheel, true);
+
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+
+ setTimeout(function() { resolveIfProcessed(resolve, 1); });
+ });
+ is(wheelEvents.length, 1,
+ "Synthesizing a wheel event via the parent process should cause 1 wheel event");
+ is(wheelEvents[0]?.deltaMode, WheelEvent.DOM_DELTA_LINE,
+ "When Synthesizing a wheel event via the parent process, the deltaMode of a wheel event should be WheelEvent.DOM_DELTA_LINE");
+ is(wheelEvents[0]?.deltaY, 3 * kLineDeltaFactor,
+ `When Synthesizing a wheel event via the parent process, the deltaY of a wheel event should be ${3 * kLineDeltaFactor}`);
+ }
+
+ const kMaxRetry = 10;
+
+ async function testTwoSingleWheelEvents() {
+ function tryIt() {
+ return new Promise(function(resolve) {
+ info("Synthesizing 2 wheel events via the parent process...");
+ wheelEvents = [];
+ var element = document.getElementById("wheelEventReceiver");
+ element.addEventListener("wheel", mousewheel, true);
+
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+
+ setTimeout(function() { resolveIfProcessed(resolve, 1); });
+ });
+ }
+ for (let i = 0; i < kMaxRetry; i++) {
+ await tryIt();
+ if (wheelEvents.length == 2) {
+ // Even if failed to coalescing, the sum of deltaY values should be same
+ // as sum of the synthesized ones.
+ if (!checkWheelEvents(WheelEvent.DOM_DELTA_LINE, 6,
+ "synthesizing 2 wheel events via the parent process")) {
+ return;
+ }
+ continue; // retry
+ }
+ is(wheelEvents.length, 1,
+ "Synthesizing 2 wheel events via the parent process should cause only 1 wheel event");
+ checkWheelEvents(WheelEvent.DOM_DELTA_LINE, 6,
+ "synthesizing 2 wheel events via the parent process");
+ return;
+ }
+ }
+
+ async function testManySingleWheelEvents() {
+ function tryIt() {
+ return new Promise(function(resolve) {
+ info("Synthesizing 5 wheel events via the parent process...");
+ wheelEvents = [];
+ var element = document.getElementById("wheelEventReceiver");
+ element.addEventListener("wheel", mousewheel, true);
+
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+
+ setTimeout(function() { resolveIfProcessed(resolve, 1); });
+ });
+ }
+ for (let i = 0; i < kMaxRetry; i++) {
+ await tryIt();
+ if (wheelEvents.length > 1 && wheelEvents.length <= 5) {
+ // Even if failed to coalescing, the sum of deltaY values should be same
+ // as sum of the synthesized ones.
+ if (!checkWheelEvents(WheelEvent.DOM_DELTA_LINE, 15,
+ "synthesizing 5 wheel events via the parent process")) {
+ return;
+ }
+ continue; // retry
+ }
+ is(wheelEvents.length, 1,
+ "Synthesizing 5 wheel events via the parent process should cause only 1 wheel event");
+ checkWheelEvents(WheelEvent.DOM_DELTA_LINE, 15,
+ "synthesizing 5 wheel events via the parent process");
+ return;
+ }
+ }
+
+ async function testMixedWheelEvents() {
+ function tryIt() {
+ return new Promise(function(resolve) {
+ info("Synthesizing 2 line wheel events, 1 page wheel event and 1 line wheel event...");
+ wheelEvents = [];
+ var element = document.getElementById("wheelEventReceiver");
+ element.addEventListener("wheel", mousewheel, true);
+
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_PAGE });
+ synthesizeWheel(element, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+
+ setTimeout(function() { resolveIfProcessed(resolve, 3); });
+ });
+ }
+ function checkGroupsOfWheelEvents() {
+ let succeeded = true;
+ let deltaY = [0, 0, 0];
+ let index = 0;
+ const description = "synthesizing 2 line wheel events, 1 page wheel event and 1 line wheel event";
+ for (const wheelEvent of wheelEvents) {
+ switch (index) {
+ case 0:
+ if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE) {
+ is(wheelEvent.deltaMode, WheelEvent.DOM_DELTA_LINE,
+ `When ${description}, the deltaMode of the first group should be WheelEvent.DOM_DELTA_LINE`);
+ break;
+ }
+ index++;
+ // fallthrough
+ case 1:
+ if (wheelEvent.deltaMode === WheelEvent.DOM_DELTA_PAGE) {
+ is(wheelEvent.deltaMode, WheelEvent.DOM_DELTA_PAGE,
+ `When ${description}, the deltaMode of the seconde group should be WheelEvent.DOM_DELTA_PAGE`);
+ break;
+ }
+ index++;
+ // fallthrough
+ case 2:
+ succeeded &= wheelEvent.deltaMode === WheelEvent.DOM_DELTA_LINE;
+ is(wheelEvent.deltaMode, WheelEvent.DOM_DELTA_LINE,
+ `When ${description}, the deltaMode of the last group should be WheelEvent.DOM_DELTA_LINE`);
+ break;
+ }
+ deltaY[index] += wheelEvent.deltaY;
+ }
+ succeeded &= deltaY == [6 * kLineDeltaFactor, 3, 3 * kLineDeltaFactor];
+ isDeeply(deltaY, [6 * kLineDeltaFactor, 3, 3 * kLineDeltaFactor],
+ `When ${description}, sum of the deltaY of the each wheel event group should be same as sum of the synthesized ones`);
+ return succeeded;
+ }
+ for (let i = 0; i < kMaxRetry; i++) {
+ await tryIt();
+ if (wheelEvents.length != 3 && wheelEvents.length > 1 && wheelEvents.length <= 5) {
+ // Even if failed to coalescing, the sum of deltaY values should be same
+ // as sum of the synthesized ones and the wheel events shouldn't be
+ // reordered.
+ if (!checkGroupsOfWheelEvents()) {
+ return;
+ }
+ continue; // retry
+ }
+ is(wheelEvents.length, 3,
+ "Synthesizing 2 line wheel events, 1 page wheel event and 1 line wheel event via the parent process should cause only 3 wheel events");
+ checkGroupsOfWheelEvents();
+ return;
+ }
+ }
+
+ async function runTests() {
+ var enabled = SpecialPowers.getDOMWindowUtils(window).asyncPanZoomEnabled;
+
+ // Avoid synthesized mousemove events to be fired at the system cursor.
+ await promiseNativeMouseEvent({
+ type: "mousemove",
+ screenX: 0,
+ screenY: 0,
+ });
+
+ await SpecialPowers.pushPrefEnv({set: [
+ ["test.events.async.enabled", true],
+ ["dom.event.wheel-coalesced.testing", true],
+ ]});
+ await promiseElementReadyForUserInput(document.documentElement);
+ await SpecialPowers.pushPrefEnv({clear: [["test.events.async.enabled"]]});
+
+ await testOneSingleWheelEvent();
+ await testTwoSingleWheelEvents();
+ await testManySingleWheelEvents();
+ await testMixedWheelEvents();
+
+ setTimeout(SimpleTest.finish);
+ window.close();
+ }
+
+ </script>
+</head>
+<body onload="SimpleTest.waitForFocus(runTests);">
+ <div id="wheelEventReceiver" style="width:100px;height:100px;"></div>
+</body>
+</html>
diff --git a/dom/events/test/test_coalesce_touchmove.html b/dom/events/test/test_coalesce_touchmove.html
new file mode 100644
index 0000000000..3733a035cc
--- /dev/null
+++ b/dom/events/test/test_coalesce_touchmove.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>touchmove coalescing</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ let tests = [
+ "file_coalesce_touchmove_ipc.html",
+ "file_coalesce_touchmove_browserchild.html",
+ "file_coalesce_touchmove_browserchild2.html",
+ ];
+
+ function finish() {
+ info("finish");
+ if (tests.length) {
+ SimpleTest.executeSoon(run);
+ return;
+ }
+ SimpleTest.finish();
+ }
+
+ function run() {
+ info(`run ${tests[0]}`);
+ window.open(tests.shift());
+ }
+ </script>
+</head>
+<body onload="run();">
+</body>
+</html>
diff --git a/dom/events/test/test_continuous_wheel_events.html b/dom/events/test/test_continuous_wheel_events.html
new file mode 100644
index 0000000000..a922041449
--- /dev/null
+++ b/dom/events/test/test_continuous_wheel_events.html
@@ -0,0 +1,3291 @@
+<!DOCTYPE HTML>
+<html style="font-size: 32px;">
+<head>
+ <title>Test for D3E WheelEvent</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="bodyLoaded()">
+<p id="display"></p>
+<div id="scrollable" style="font-family: 'Courier New', monospace; font-size: 18px; line-height: 1; overflow: auto; width: 200px; height: 200px;">
+ <div id="scrolled" style="font-size: 64px; width: 5000px; height: 5000px;">
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ </div>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+var gScrollableElement;
+var gScrolledElement;
+
+SimpleTest.waitForExplicitFinish();
+function bodyLoaded() {
+ gScrollableElement = document.getElementById("scrollable");
+ gScrolledElement = document.getElementById("scrolled");
+ runTests();
+}
+
+var gLineHeight = 0;
+var gHorizontalLine = 0;
+var gPageHeight = 0;
+var gPageWidth = 0;
+
+function sendWheelAndWait(aX, aY, aEvent)
+{
+ sendWheelAndPaint(gScrollableElement, aX, aY, aEvent, continueTest);
+}
+
+function* prepareScrollUnits()
+{
+ var result = -1;
+ function handler(aEvent)
+ {
+ result = aEvent.detail;
+ aEvent.preventDefault();
+ }
+ window.addEventListener("MozMousePixelScroll", handler, { capture: true, passive: false });
+
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gLineHeight = result;
+ ok(gLineHeight > 10 && gLineHeight < 25, "prepareScrollUnits: gLineHeight may be illegal value, got " + gLineHeight);
+
+ result = -1;
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gHorizontalLine = result;
+ ok(gHorizontalLine > 5 && gHorizontalLine < 16, "prepareScrollUnits: gHorizontalLine may be illegal value, got " + gHorizontalLine);
+
+ result = -1;
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gPageHeight = result;
+ // XXX Cannot we know the actual scroll port size?
+ ok(gPageHeight >= 150 && gPageHeight <= 200,
+ "prepareScrollUnits: gPageHeight is strange value, got " + gPageHeight);
+
+ result = -1;
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gPageWidth = result;
+ ok(gPageWidth >= 150 && gPageWidth <= 200,
+ "prepareScrollUnits: gPageWidth is strange value, got " + gPageWidth);
+
+ window.removeEventListener("MozMousePixelScroll", handler, true);
+}
+
+// Tests continuous trusted wheel events. Trusted wheel events should cause
+// legacy mouse scroll events when its lineOrPageDelta value is not zero or
+// accumulated delta values of pixel scroll events of pixel only device
+// become over the line height.
+function* testContinuousTrustedEvents()
+{
+ const kSynthesizedWheelEventTests = [
+ { description: "Simple horizontal wheel event by pixels (16.0 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 16 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by pixels (16.0 - 1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 16 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by pixels (16.0 - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 16 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple vertical wheel event by pixels (16.0 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 16.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 16.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 16 } }
+ },
+ { description: "Simple vertical wheel event by pixels (16.0 - 1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 16.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 16.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 16 } }
+ },
+ { description: "Simple vertical wheel event by pixels (16.0 - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 16.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 16.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 16 } }
+ },
+
+ { description: "Simple z-direction wheel event by pixels (16.0 - 1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple horizontal wheel event by pixels (-16.0 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -16.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -16 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by pixels (-16.0 - -1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -16.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -16 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by pixels (-16.0 - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -16.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -16 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple vertical wheel event by pixels (-16.0 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -16.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -16.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -16 } }
+ },
+ { description: "Simple vertical wheel event by pixels (-16.0 - -1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -16.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -16.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -16 } }
+ },
+ { description: "Simple vertical wheel event by pixels (-16.0 - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -16.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -16.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -16 } }
+ },
+
+ { description: "Simple z-direction wheel event by pixels (-16.0 - -1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: -16.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: -16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ // 3 scroll events per line, and legacy line scroll will be fired first.
+ { description: "Horizontal wheel event by pixels (5.3 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Vertical wheel event by pixels (5.3 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Vertical wheel event by pixels (5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Vertical wheel event by pixels (5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+
+ { description: "Horizontal wheel event by pixels (-5.3 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (-5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (-5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Vertical wheel event by pixels (-5.3 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Vertical wheel event by pixels (-5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Vertical wheel event by pixels (-5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+
+ // 3 scroll events per line, and legacy line scroll will be fired last.
+ { description: "Horizontal wheel event by pixels (5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (5.3 - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Vertical wheel event by pixels (5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Vertical wheel event by pixels (5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Vertical wheel event by pixels (5.3 - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+
+ { description: "Horizontal wheel event by pixels (-5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (-5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Horizontal wheel event by pixels (-5.3 - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Vertical wheel event by pixels (-5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Vertical wheel event by pixels (-5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Vertical wheel event by pixels (-5.3 - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+
+ // Oblique scroll.
+ { description: "To bottom-right wheel event by pixels (5.3/5.2 - 1/1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 5.2, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 5.2, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "To bottom-right wheel event by pixels (5.3/5.2 - 0/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 5.2, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 5.2, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "To bottom-right wheel event by pixels (5.3/5.2 - 0/0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 5.2, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 5.2, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+
+ { description: "To bottom-left wheel event by pixels (-5.3/5.3 - -1/1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "To bottom-left wheel event by pixels (-5.3/5.3 - 0/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "To bottom-left wheel event by pixels (-5.3/5.3 - 0/0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+
+ { description: "To top-left wheel event by pixels (-5.2/-5.3 - -1/-1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.2, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.2, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "To top-left wheel event by pixels (-5.2/-5.3 - 0/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.2, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.2, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "To top-left wheel event by pixels (-5.2/-5.3 - 0/0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.2, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.2, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+
+ { description: "To top-right wheel event by pixels (5.3/-5.3 - 1/-1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "To top-right wheel event by pixels (5.3/-5.3 - 0/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+
+ // Pixel scroll only device's test. the lineOrPageDelta values should be computed
+ // by ESM. When changing the direction for each delta value, it should be
+ // reset at that time.
+ { description: "Pixel only device's horizontal wheel event by pixels (5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Pixel only device's horizontal wheel event by pixels (5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Pixel only device's horizontal wheel event by pixels (5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (5.3 - 0) #4",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 1.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (5.3 - 1) #5",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 5 } }
+ },
+
+ { description: "Pixel only device's horizontal wheel event by pixels (-5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Pixel only device's horizontal wheel event by pixels (-5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Pixel only device's horizontal wheel event by pixels (-5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Pixel only device's Vertical wheel event by pixels (-5.3 - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (-5.3 - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (-5.3 - 0) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (-5.3 - 0) #4",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -1.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } }
+ },
+ { description: "Pixel only device's Vertical wheel event by pixels (-5.3 - -1) #5",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -5.3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -5 } }
+ },
+
+ // ESM should reset an accumulated delta value only when the direction of it
+ // is changed but shouldn't reset the other delta.
+ { description: "Pixel only device's bottom-right wheel event by pixels (5.3/4.9 - 0/0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: 4 } }
+ },
+ { description: "Pixel only device's bottom-right wheel event by pixels (5.3/4.9 - 0/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: 4 } }
+ },
+ { description: "Pixel only device's bottom-left wheel event by pixels (-5.3/4.9 - 0/0) #4",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: 4 } }
+ },
+ // the accumulated X should be 0 here, but Y shouldn't be reset.
+ { description: "Pixel only device's bottom-right wheel event by pixels (5.3/4.9 - 0/0) #5",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 5.3, deltaY: 1.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 5.3, deltaY: 1.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 5 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } }
+ },
+
+ { description: "Pixel only device's top-left wheel event by pixels (-5.3/-4.9 - 0/0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: -4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: -4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: -4 } }
+ },
+ { description: "Pixel only device's top-left wheel event by pixels (-5.3/-4.9 - 0/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: -4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: -4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: -4 } }
+ },
+ { description: "Pixel only device's bottom-left wheel event by pixels (-5.3/4.9 - 0/0) #4",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: 4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: 4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: 4 } }
+ },
+ // the accumulated Y should be 0 here, but X shouldn't be reset.
+ { description: "Pixel only device's top-left wheel event by pixels (-5.3/-4.9 - 0/0) #5",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -5.3, deltaY: -4.9, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: true,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -5.3, deltaY: -4.9, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -5 },
+ vertical: { expected: true, preventDefault: false, detail: -4 } }
+ },
+
+ // Simple line scroll tests.
+ { description: "Simple horizontal wheel event by lines (1.0 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gHorizontalLine },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by lines (1.0 - 1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gHorizontalLine },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple horizontal wheel event by lines (-1.0 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -gHorizontalLine },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by lines (-1.0 - -1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -gHorizontalLine },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple vertical wheel event by lines (-1.0 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -gLineHeight } }
+ },
+ { description: "Simple vertical wheel event by lines (-1.0 - -1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -gLineHeight } }
+ },
+
+ { description: "Simple vertical wheel event by lines (1.0 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: gLineHeight } }
+ },
+ { description: "Simple vertical wheel event by lines (1.0 - 1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: gLineHeight } }
+ },
+
+ // high resolution line scroll
+ { description: "High resolution horizontal wheel event by lines (0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by lines (0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by lines (0.333... - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "High resolution horizontal wheel event by lines (-0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -Math.floor(gHorizontalLine / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by lines (-0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -Math.floor(gHorizontalLine / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by lines (-0.333... - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -Math.floor(gHorizontalLine / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "High resolution vertical wheel event by lines (0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by lines (0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by lines (0.333... - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 3) } }
+ },
+
+ { description: "High resolution vertical wheel event by lines (-0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -Math.floor(gLineHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by lines (-0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -Math.floor(gLineHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by lines (-0.333... - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -Math.floor(gLineHeight / 3) } }
+ },
+
+ // Oblique line scroll
+ { description: "Oblique wheel event by lines (-1.0/2.0 - -1/2)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: 2.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 2, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0, deltaY: 2.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: true, preventDefault: false, detail: 2 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -gHorizontalLine },
+ vertical: { expected: true, preventDefault: false, detail: gLineHeight * 2 } }
+ },
+
+ { description: "Oblique wheel event by lines (1.0/-2.0 - 1/-2)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: -2.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: -2, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: -2.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: -2 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: false, detail: -gLineHeight * 2 } }
+ },
+
+ { description: "High resolution oblique wheel event by lines (0.5/0.333.../-0.8 - 0/0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 3) } }
+ },
+ { description: "High resolution oblique wheel event by lines (0.5/0.333.../-0.8 - 1/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 3) } }
+ },
+ { description: "High resolution oblique wheel event by lines (0.5/0.333.../-0.8 - 0/1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 3) } }
+ },
+
+ // Simple page scroll tests.
+ { description: "Simple horizontal wheel event by pages (1.0 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gPageWidth },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by pages (1.0 - 1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gPageWidth },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple horizontal wheel event by pages (-1.0 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -gPageWidth },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "Simple horizontal wheel event by pages (-1.0 - -1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -gPageWidth },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "Simple vertical wheel event by pages (-1.0 - -1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -gPageHeight } }
+ },
+ { description: "Simple vertical wheel event by pages (-1.0 - -1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -gPageHeight } }
+ },
+
+ { description: "Simple vertical wheel event by pages (1.0 - 1) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: gPageHeight } }
+ },
+ { description: "Simple vertical wheel event by pages (1.0 - 1) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: gPageHeight } }
+ },
+
+ // high resolution page scroll
+ { description: "High resolution horizontal wheel event by pages (0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by pages (0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by pages (0.333... - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "High resolution horizontal wheel event by pages (-0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -Math.floor(gPageWidth / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by pages (-0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -Math.floor(gPageWidth / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+ { description: "High resolution horizontal wheel event by pages (-0.333... - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0 / 3, deltaY: 0.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -Math.floor(gPageWidth / 3) },
+ vertical: { expected: false, preventDefault: false, detail: 0 } }
+ },
+
+ { description: "High resolution vertical wheel event by pages (0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by pages (0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by pages (0.333... - 1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: 1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight / 3) } }
+ },
+
+ { description: "High resolution vertical wheel event by pages (-0.333... - 0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -Math.floor(gPageHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by pages (-0.333... - 0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -Math.floor(gPageHeight / 3) } }
+ },
+ { description: "High resolution vertical wheel event by pages (-0.333... - -1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.0, deltaY: -1.0 / 3, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -Math.floor(gPageHeight / 3) } }
+ },
+
+ // Oblique page scroll
+ { description: "Oblique wheel event by pages (-1.0/2.0 - -1/2)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: 2.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 2, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -1.0, deltaY: 2.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -gPageWidth },
+ vertical: { expected: true, preventDefault: false, detail: gPageHeight * 2 } }
+ },
+
+ { description: "Oblique wheel event by pages (1.0/-2.0 - 1/-2)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: -2.0, deltaZ: 0.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: -2, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: -2.0, deltaZ: 0.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: false, detail: -gPageHeight * 2 } }
+ },
+
+ { description: "High resolution oblique wheel event by pages (0.5/0.333.../-0.8 - 0/0) #1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth / 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight / 3) } }
+ },
+ { description: "High resolution oblique wheel event by pages (0.5/0.333.../-0.8 - 1/0) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth / 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight / 3) } }
+ },
+ { description: "High resolution oblique wheel event by pages (0.5/0.333.../-0.8 - 0/1) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8, isMomentum: false,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.5, deltaY: 1.0 / 3, deltaZ: -0.8
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth / 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight / 3) } }
+ },
+
+ // preventDefault() shouldn't prevent other legacy events.
+ { description: "preventDefault() shouldn't prevent other legacy events (pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 1 },
+ vertical: { expected: true, preventDefault: true, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 16 },
+ vertical: { expected: true, preventDefault: true, detail: 16 } },
+ },
+ { description: "preventDefault() shouldn't prevent other legacy events (line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 1 },
+ vertical: { expected: true, preventDefault: true, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
+ },
+ { description: "preventDefault() shouldn't prevent other legacy events (page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: true, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: true, detail: gPageHeight } },
+ },
+
+ // If wheel event is consumed by preventDefault(), legacy events are not necessary.
+ { description: "If wheel event is consumed by preventDefault(), legacy events are not necessary (pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: true,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ },
+ { description: "If wheel event is consumed by preventDefault(), legacy events are not necessary (line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: true,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ },
+ { description: "If wheel event is consumed by preventDefault(), legacy events are not necessary (page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: true,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ },
+
+ // modifier key state tests
+ { description: "modifier key tests (shift, pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 16 },
+ vertical: { expected: true, preventDefault: true, detail: 16 } },
+ },
+ { description: "modifier key tests (shift, line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
+ },
+ { description: "modifier key tests (shift, page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: true, detail: gPageHeight } },
+ },
+
+ { description: "modifier key tests (ctrl, pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 16 },
+ vertical: { expected: true, preventDefault: true, detail: 16 } },
+ },
+ { description: "modifier key tests (ctrl, line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
+ },
+ { description: "modifier key tests (ctrl, page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: true, detail: gPageHeight } },
+ },
+
+ { description: "modifier key tests (alt, pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 16 },
+ vertical: { expected: true, preventDefault: true, detail: 16 } },
+ },
+ { description: "modifier key tests (alt, line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
+ },
+ { description: "modifier key tests without content checking mode (alt, line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ skipDeltaModeCheck: true,
+ deltaX: gHorizontalLine,
+ deltaY: gLineHeight,
+ deltaZ: Math.max(gHorizontalLine, gLineHeight),
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
+ },
+ { description: "modifier key tests (alt, page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: true, detail: gPageHeight } },
+ },
+
+ { description: "modifier key tests (meta, pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: true },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: 16 },
+ vertical: { expected: true, preventDefault: true, detail: 16 } },
+ },
+ { description: "modifier key tests (meta, line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: true },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: true, detail: gLineHeight } },
+ },
+ { description: "modifier key tests (meta, page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: true,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: true, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: true, detail: gPageHeight } },
+ },
+
+ // Momentum scroll should cause legacy events.
+ { description: "Momentum scroll should cause legacy events (pixel, not momentum)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 16 },
+ vertical: { expected: true, preventDefault: false, detail: 16 } },
+ },
+ { description: "Momentum scroll should cause legacy events (pixel, momentum)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0, isMomentum: true,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 16.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 16 },
+ vertical: { expected: true, preventDefault: false, detail: 16 } },
+ },
+ { description: "Momentum scroll should cause legacy events (line, not momentum)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: false, detail: gLineHeight } },
+ },
+ { description: "Momentum scroll should cause legacy events (line, momentum)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: true,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gHorizontalLine },
+ vertical: { expected: true, preventDefault: false, detail: gLineHeight } },
+ },
+ { description: "Momentum scroll should cause legacy events (page, not momentum)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: false,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: false, detail: gPageHeight } },
+ },
+ { description: "Momentum scroll should cause legacy events (page, momentum)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, isMomentum: true,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: gPageWidth },
+ vertical: { expected: true, preventDefault: false, detail: gPageHeight } },
+ },
+
+ // Tests for accumulation delta when delta_multiplier_is customized.
+ { description: "lineOrPageDelta should be recomputed by ESM (pixel) #1",
+ prepare () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
+ ["mousewheel.default.delta_multiplier_y", 300]]},
+ continueTest);
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: gHorizontalLine / 4, deltaY: gLineHeight / 8, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: gHorizontalLine / 4 * 2, deltaY: gLineHeight / 8 * 3, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine / 4 * 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight / 8 * 3) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (pixel) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: gHorizontalLine / 4 + 1, deltaY: gLineHeight / 8 + 1, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: (gHorizontalLine / 4 + 1) * 2, deltaY: (gLineHeight / 8 + 1) * 3, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor((gHorizontalLine / 4 + 1) * 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor((gLineHeight / 8 + 1) * 3) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (pixel) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: gHorizontalLine / 4 + 1, deltaY: gLineHeight / 8 + 1, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: (gHorizontalLine / 4 + 1) * 2, deltaY: (gLineHeight / 8 + 1) * 3, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor((gHorizontalLine / 4 + 1) * 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor((gLineHeight / 8 + 1) * 3) } },
+ finished () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ },
+
+ { description: "lineOrPageDelta should be recomputed by ESM (pixel, negative, shift) #1",
+ prepare () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_shift.delta_multiplier_x", 200],
+ ["mousewheel.with_shift.delta_multiplier_y", 300]]},
+ continueTest);
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -gHorizontalLine / 4, deltaY: -gLineHeight / 8, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -gHorizontalLine / 4 * 2, deltaY: -gLineHeight / 8 * 3, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(-gHorizontalLine / 4 * 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.ceil(-gLineHeight / 8 * 3) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (pixel, negative, shift) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -(gHorizontalLine / 4 + 1), deltaY: -(gLineHeight / 8 + 1), deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -(gHorizontalLine / 4 + 1) * 2, deltaY: -(gLineHeight / 8 + 1) * 3, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(-(gHorizontalLine / 4 + 1) * 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.ceil(-(gLineHeight / 8 + 1) * 3) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (pixel, negative, shift) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -(gHorizontalLine / 4 + 1), deltaY: -(gLineHeight / 8 + 1), deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -(gHorizontalLine / 4 + 1) * 2, deltaY: -(gLineHeight / 8 + 1) * 3, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: -1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(-(gHorizontalLine / 4 + 1) * 2) },
+ vertical: { expected: true, preventDefault: false, detail: Math.ceil(-(gLineHeight / 8 + 1) * 3) } },
+ finished () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.with_shift.delta_multiplier_x", 100],
+ ["mousewheel.with_shift.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ },
+
+ { description: "lineOrPageDelta should be recomputed by ESM (line) #1",
+ prepare () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
+ ["mousewheel.default.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.6, deltaY: 0.4, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine * 0.6) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (line) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.6, deltaY: 0.4, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: 1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine * 0.6) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (line) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.6, deltaY: 0.4, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gHorizontalLine * 0.6) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
+ finished () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ },
+
+ { description: "lineOrPageDelta should be recomputed by ESM (line, negative) #1",
+ prepare () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 200],
+ ["mousewheel.default.delta_multiplier_y", -100]]},
+ continueTest);
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -0.6, deltaY: 0.4, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gHorizontalLine * -0.6) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (line, negative) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -0.6, deltaY: 0.4, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: -1 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gHorizontalLine * -0.6) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (line, negative) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -0.6, deltaY: 0.4, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: 1 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gHorizontalLine * -0.6) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gLineHeight * 0.4) } },
+ finished () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ },
+
+ { description: "lineOrPageDelta should be recomputed by ESM (page) #1",
+ prepare () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 200]]},
+ continueTest);
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.3, deltaY: 0.8, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth * 0.3) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight * 0.8) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (page) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.3, deltaY: 0.4, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.3, deltaY: 0.8, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth * 0.3) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight * 0.8) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (page) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.4, deltaY: 0.4, deltaZ: 0,
+ lineOrPageDeltaX: 3, lineOrPageDeltaY: 5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: 0.4, deltaY: 0.8, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_DOWN } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.floor(gPageWidth * 0.4) },
+ vertical: { expected: true, preventDefault: false, detail: Math.floor(gPageHeight * 0.8) } },
+ finished () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ },
+
+ { description: "lineOrPageDelta should be recomputed by ESM (page, negative) #1",
+ prepare () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 200]]},
+ continueTest);
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -0.3, deltaY: -0.8, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: false, preventDefault: false, detail: 0 } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gPageWidth * -0.3) },
+ vertical: { expected: true, preventDefault: false, detail: Math.ceil(gPageHeight * -0.8) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (page, negative) #2",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.3, deltaY: -0.4, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -0.3, deltaY: -0.8, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: false, preventDefault: false, detail: 0 },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gPageWidth * -0.3) },
+ vertical: { expected: true, preventDefault: false, detail: Math.ceil(gPageHeight * -0.8) } },
+ },
+ { description: "lineOrPageDelta should be recomputed by ESM (page, negative) #3",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.4, deltaY: -0.4, deltaZ: 0,
+ lineOrPageDeltaX: -3, lineOrPageDeltaY: -5, isNoLineOrPageDelta: false,
+ isCustomizedByPrefs: false,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ wheel: {
+ expected: true, preventDefault: false,
+ deltaX: -0.4, deltaY: -0.8, deltaZ: 0
+ },
+ DOMMouseScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP },
+ vertical: { expected: true, preventDefault: false, detail: UIEvent.SCROLL_PAGE_UP } },
+ MozMousePixelScroll: {
+ horizontal: { expected: true, preventDefault: false, detail: Math.ceil(gPageWidth * -0.4) },
+ vertical: { expected: true, preventDefault: false, detail: Math.ceil(gPageHeight * -0.8) } },
+ finished () {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100]]},
+ continueTest);
+ },
+ },
+ ];
+
+ var currentWheelEventTest;
+ var calledHandlers = { wheel: false,
+ DOMMouseScroll: { horizontal: false, vertical: false },
+ MozMousePixelScroll: { horizontal: false, vertical: false } };
+
+ function wheelEventHandler(aEvent)
+ {
+ var description = "testContinuousTrustedEvents, ";
+ description += currentWheelEventTest.description + ": wheel event ";
+
+ ok(!calledHandlers.wheel,
+ description + "was fired twice or more");
+ calledHandlers.wheel = true;
+
+ is(aEvent.target, gScrolledElement,
+ description + "target was invalid");
+ if (!currentWheelEventTest.wheel.skipDeltaModeCheck) {
+ is(aEvent.deltaMode, currentWheelEventTest.event.deltaMode,
+ description + "deltaMode was invalid");
+ }
+ is(SpecialPowers.wrap(aEvent).deltaMode, currentWheelEventTest.event.deltaMode,
+ description + "deltaMode is raw value from privileged script");
+ for (let prop of ["deltaX", "deltaY", "deltaZ"]) {
+ is(aEvent[prop], currentWheelEventTest.wheel[prop],
+ description + prop + " was invalid");
+ if (currentWheelEventTest.wheel.skipDeltaModeCheck) {
+ is(aEvent.deltaMode, WheelEvent.DOM_DELTA_PIXEL,
+ description + "deltaMode should become pixels for line scrolling if unchecked by content")
+ if (aEvent[prop] != 0) {
+ isnot(aEvent[prop], SpecialPowers.wrap(aEvent)[prop],
+ description + "should keep returning raw value for privileged script");
+ }
+ }
+ }
+ is(aEvent.shiftKey, currentWheelEventTest.event.shiftKey,
+ description + "shiftKey was invalid");
+ is(aEvent.ctrlKey, currentWheelEventTest.event.ctrlKey,
+ description + "ctrlKey was invalid");
+ is(aEvent.altKey, currentWheelEventTest.event.altKey,
+ description + "shiftKey was invalid");
+ is(aEvent.metaKey, currentWheelEventTest.event.metaKey,
+ description + "metaKey was invalid");
+
+ ok(!aEvent.defaultPrevented,
+ description + "defaultPrevented should be false");
+ if (currentWheelEventTest.wheel.preventDefault) {
+ aEvent.preventDefault();
+ ok(aEvent.defaultPrevented,
+ description + "defaultPrevented should be true");
+ }
+ }
+
+ function legacyEventHandler(aEvent)
+ {
+ var description = "testContinuousTrustedEvents, ";
+ description += currentWheelEventTest.description + ": " + aEvent.type + " event ";
+
+ if (aEvent.axis != MouseScrollEvent.HORIZONTAL_AXIS &&
+ aEvent.axis != MouseScrollEvent.VERTICAL_AXIS) {
+ ok(false,
+ description + "had invalid axis (" + aEvent.axis + ")");
+ return;
+ }
+
+ var isHorizontal = (aEvent.axis == MouseScrollEvent.HORIZONTAL_AXIS);
+
+ description += isHorizontal ? "(horizontal) " : "(vertical) ";
+
+ var isScrollEvent = (aEvent.type == "DOMMouseScroll");
+ var expectedEvent =
+ isScrollEvent ? currentWheelEventTest.DOMMouseScroll :
+ currentWheelEventTest.MozMousePixelScroll;
+ var expected =
+ isHorizontal ? expectedEvent.horizontal : expectedEvent.vertical;
+
+ if (aEvent.type == "DOMMouseScroll") {
+ if (isHorizontal) {
+ ok(!calledHandlers.DOMMouseScroll.horizontal,
+ description + "was fired twice or more");
+ calledHandlers.DOMMouseScroll.horizontal = true;
+ } else {
+ ok(!calledHandlers.DOMMouseScroll.vertical,
+ description + "was fired twice or more");
+ calledHandlers.DOMMouseScroll.vertical = true;
+ }
+ } else {
+ if (isHorizontal) {
+ ok(!calledHandlers.MozMousePixelScroll.horizontal,
+ description + "was fired twice or more");
+ calledHandlers.MozMousePixelScroll.horizontal = true;
+ } else {
+ ok(!calledHandlers.MozMousePixelScroll.vertical,
+ description + "was fired twice or more");
+ calledHandlers.MozMousePixelScroll.vertical = true;
+ }
+ }
+
+ is(aEvent.target, gScrolledElement,
+ description + "target was invalid");
+ is(aEvent.detail, expected.detail,
+ description + "detail was invalid");
+
+ is(aEvent.shiftKey, currentWheelEventTest.event.shiftKey,
+ description + "shiftKey was invalid");
+ is(aEvent.ctrlKey, currentWheelEventTest.event.ctrlKey,
+ description + "ctrlKey was invalid");
+ is(aEvent.altKey, currentWheelEventTest.event.altKey,
+ description + "shiftKey was invalid");
+ is(aEvent.metaKey, currentWheelEventTest.event.metaKey,
+ description + "metaKey was invalid");
+
+ var expectedDefaultPrevented =
+ isScrollEvent ? false :
+ isHorizontal ? currentWheelEventTest.DOMMouseScroll.horizontal.preventDefault :
+ currentWheelEventTest.DOMMouseScroll.vertical.preventDefault;
+ is(aEvent.defaultPrevented, expectedDefaultPrevented,
+ description + "defaultPrevented should be " + expectedDefaultPrevented);
+
+ if (expected.preventDefault) {
+ aEvent.preventDefault();
+ ok(aEvent.defaultPrevented,
+ description + "defaultPrevented should be true");
+ }
+ }
+
+ window.addEventListener("wheel", wheelEventHandler, { capture: true, passive: false });
+ window.addEventListener("DOMMouseScroll", legacyEventHandler, { capture: true, passive: false });
+ window.addEventListener("MozMousePixelScroll", legacyEventHandler, { capture: true, passive: false });
+
+ for (var i = 0; i < kSynthesizedWheelEventTests.length; i++) {
+ gScrollableElement.scrollTop = gScrollableElement.scrollBottom = 1000;
+
+ currentWheelEventTest = kSynthesizedWheelEventTests[i];
+
+ if (currentWheelEventTest.prepare) {
+ yield currentWheelEventTest.prepare();
+ }
+
+ yield sendWheelAndWait(10, 10, currentWheelEventTest.event);
+
+ if (currentWheelEventTest.finished) {
+ yield currentWheelEventTest.finished();
+ }
+
+ var description = "testContinuousTrustedEvents, " +
+ currentWheelEventTest.description + ": ";
+ is(calledHandlers.wheel, currentWheelEventTest.wheel.expected,
+ description + "wheel event was fired or not fired");
+ is(calledHandlers.DOMMouseScroll.horizontal,
+ currentWheelEventTest.DOMMouseScroll.horizontal.expected,
+ description + "horizontal DOMMouseScroll event was fired or not fired");
+ is(calledHandlers.DOMMouseScroll.vertical,
+ currentWheelEventTest.DOMMouseScroll.vertical.expected,
+ description + "vertical DOMMouseScroll event was fired or not fired");
+ is(calledHandlers.MozMousePixelScroll.horizontal,
+ currentWheelEventTest.MozMousePixelScroll.horizontal.expected,
+ description + "horizontal MozMousePixelScroll event was fired or not fired");
+ is(calledHandlers.MozMousePixelScroll.vertical,
+ currentWheelEventTest.MozMousePixelScroll.vertical.expected,
+ description + "vertical MozMousePixelScroll event was fired or not fired");
+
+ calledHandlers = { wheel: false,
+ DOMMouseScroll: { horizontal: false, vertical: false },
+ MozMousePixelScroll: { horizontal: false, vertical: false } };
+ }
+
+ window.removeEventListener("wheel", wheelEventHandler, true);
+ window.removeEventListener("DOMMouseScroll", legacyEventHandler, true);
+ window.removeEventListener("MozMousePixelScroll", legacyEventHandler, true);
+}
+
+var gTestContinuation = null;
+
+function continueTest()
+{
+ if (!gTestContinuation) {
+ gTestContinuation = testBody();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ }
+}
+
+function* testBody()
+{
+ yield* prepareScrollUnits();
+ yield* testContinuousTrustedEvents();
+}
+
+function runTests()
+{
+ SpecialPowers.pushPrefEnv({"set": [
+ // FIXME(emilio): This test is broken in HiDPI, unclear if
+ // MozMousePixelScroll is not properly converting to CSS pixels, or
+ // whether sendWheelAndWait expectes device rather than CSS pixels, or
+ // something else.
+ ["layout.css.devPixelsPerPx", 1.0],
+
+ ["dom.event.wheel-deltaMode-lines.disabled", true],
+
+ ["mousewheel.system_scroll_override.enabled", false],
+
+ ["mousewheel.transaction.timeout", 100000],
+ ["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100],
+ ["mousewheel.default.delta_multiplier_z", 100],
+ ["mousewheel.with_alt.delta_multiplier_x", 100],
+ ["mousewheel.with_alt.delta_multiplier_y", 100],
+ ["mousewheel.with_alt.delta_multiplier_z", 100],
+ ["mousewheel.with_control.delta_multiplier_x", 100],
+ ["mousewheel.with_control.delta_multiplier_y", 100],
+ ["mousewheel.with_control.delta_multiplier_z", 100],
+ ["mousewheel.with_meta.delta_multiplier_x", 100],
+ ["mousewheel.with_meta.delta_multiplier_y", 100],
+ ["mousewheel.with_meta.delta_multiplier_z", 100],
+ ["mousewheel.with_shift.delta_multiplier_x", 100],
+ ["mousewheel.with_shift.delta_multiplier_y", 100],
+ ["mousewheel.with_shift.delta_multiplier_z", 100],
+
+ ["test.events.async.enabled", true]
+ ]}, continueTest);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_dblclick_explicit_original_target.html b/dom/events/test/test_dblclick_explicit_original_target.html
new file mode 100644
index 0000000000..214ae48dea
--- /dev/null
+++ b/dom/events/test/test_dblclick_explicit_original_target.html
@@ -0,0 +1,52 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test explicit original target of dblclick event</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>
+<p id="display">Test explicit original target of dblclick event</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runTests()
+{
+ synthesizeMouse(document.getElementById("display"), 5, 5, { clickCount: 2 });
+}
+
+window.onmousedown = function(event) {
+ is(event.explicitOriginalTarget.nodeType, Node.TEXT_NODE,
+ "explicitOriginalTarget is a text node");
+ is(event.explicitOriginalTarget, document.getElementById("display").firstChild,
+ "explicitOriginalTarget should point to the child node of the click target");
+}
+
+window.onmouseup = function(event) {
+ is(event.explicitOriginalTarget.nodeType, Node.TEXT_NODE,
+ "explicitOriginalTarget is a text node");
+ is(event.explicitOriginalTarget, document.getElementById("display").firstChild,
+ "explicitOriginalTarget should point to the child node of the click target");
+}
+
+// The old versions of Gecko had explicitOriginalTarget pointing to a Text node
+// when handling *click events, newer versions target Elements.
+window.ondblclick = function(event) {
+ is(event.explicitOriginalTarget.nodeType, Node.ELEMENT_NODE,
+ "explicitOriginalTarget is an element node");
+ is(event.explicitOriginalTarget, document.getElementById("display"),
+ "explicitOriginalTarget should point to the click target");
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_deltaMode_lines_always_enabled.html b/dom/events/test/test_deltaMode_lines_always_enabled.html
new file mode 100644
index 0000000000..b1c2f44046
--- /dev/null
+++ b/dom/events/test/test_deltaMode_lines_always_enabled.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Test for always-enabling lines deltaMode</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<p id="display"></p>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1684001">Mozilla Bug 1684001</a>
+<div id="overflow" style="height: 300vh"></div>
+<pre id="test"></pre>
+<script>
+SimpleTest.waitForExplicitFinish();
+
+const HACK_PREF = "dom.event.wheel-deltaMode-lines.always-enabled";
+
+(async function() {
+ await SimpleTest.promiseFocus(window);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ [HACK_PREF, document.domain],
+ ["test.events.async.enabled", true],
+ ],
+ });
+ // FYI: Cannot use scrollingElement because it's center may be outside of the viewport.
+ await promiseElementReadyForUserInput(document.querySelector("a[href]"));
+ await SpecialPowers.pushPrefEnv({
+ clear: [
+ ["test.events.async.enabled"],
+ ],
+ });
+
+ document.addEventListener("wheel", e => {
+ e.deltaY; /* access the delta unchecked */
+ is(e.deltaMode, WheelEvent.DOM_DELTA_LINE, "Accessing wheel event data should return lines even if unchecked, if the pref is enabled for that domain");
+ SimpleTest.finish();
+ }, { once: true });
+
+ synthesizeWheel(document.scrollingElement, 10, 10, { deltaY: 3.0, deltaMode: WheelEvent.DOM_DELTA_LINE });
+}());
+</script>
diff --git a/dom/events/test/test_deviceSensor.html b/dom/events/test/test_deviceSensor.html
new file mode 100644
index 0000000000..c1b032f1b5
--- /dev/null
+++ b/dom/events/test/test_deviceSensor.html
@@ -0,0 +1,136 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=402089
+-->
+<head>
+ <title>Test for Bug 742376</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=742376">Mozilla Bug 742376</a>
+<script class="testbody" type="text/javascript">
+
+/** Test for Bug 742376 **/
+let Cc = SpecialPowers.Cc;
+let Ci = SpecialPowers.Ci;
+let dss = Cc["@mozilla.org/devicesensors;1"].getService(Ci.nsIDeviceSensors);
+
+function hasLightListeners() {
+ return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_LIGHT, window);
+}
+
+function hasOrientationListeners() {
+ return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ORIENTATION, window) ||
+ dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ROTATION_VECTOR, window) ||
+ dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GAME_ROTATION_VECTOR, window);
+}
+
+function hasProximityListeners() {
+ return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_PROXIMITY, window);
+}
+
+function hasMotionListeners() {
+ return dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_ACCELERATION, window) ||
+ dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_LINEAR_ACCELERATION, window) ||
+ dss.hasWindowListener(Ci.nsIDeviceSensorData.TYPE_GYROSCOPE, window);
+}
+
+async function test_event_presence(prefName, eventCheck, eventName) {
+ function dumbListener(event) {}
+ function dumbListener2(event) {}
+ function dumbListener3(event) {}
+
+ await SpecialPowers.pushPrefEnv({"set": [
+ [prefName, true]
+ ]});
+
+ is(eventCheck(), false, "Must not have listeners before tests start");
+
+ window.addEventListener(eventName, dumbListener);
+ window.addEventListener("random_event_name", function() {});
+ window.addEventListener(eventName, dumbListener2);
+
+ is(eventCheck(), true, `Should have listeners when ${eventName} sensor is enabled`);
+
+ window.removeEventListener(eventName, dumbListener);
+ window.removeEventListener(eventName, dumbListener2);
+
+ is(eventCheck(), false, "Must not have listeners when removed");
+
+ await SpecialPowers.pushPrefEnv({"set": [
+ [prefName, false]
+ ]});
+
+ window.addEventListener(eventName, dumbListener);
+ window.addEventListener("random_event_name", function() {});
+ window.addEventListener(eventName, dumbListener2);
+
+ is(eventCheck(), false, "Must not have listeners when sensor is disabled");
+}
+
+async function start() {
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["device.sensors.enabled", true],
+ ["device.sensors.orientation.enabled", true]
+ ]});
+
+ is(hasOrientationListeners(), false, "Must not have listeners before tests start");
+
+ function dumbListener(event) {}
+ function dumbListener2(event) {}
+ function dumbListener3(event) {}
+
+ window.addEventListener("deviceorientation", dumbListener);
+ window.addEventListener("random_event_name", function() {});
+ window.addEventListener("deviceorientation", dumbListener2);
+
+ is(hasOrientationListeners(), true, "Listeners should have been added");
+
+ await new Promise(resolve => {
+ window.setTimeout(function() {
+ window.removeEventListener("deviceorientation", dumbListener);
+ is(hasOrientationListeners(), true, "Only some listeners should have been removed");
+ window.setTimeout(function() {
+ window.removeEventListener("deviceorientation", dumbListener2);
+ window.setTimeout(function() {
+ is(hasOrientationListeners(), false, "Listeners should have been removed");
+ resolve();
+ }, 0);
+ }, 0);
+ }, 0);
+ });
+
+ await new Promise(resolve => {
+ window.ondeviceorientation = function() {}
+ window.setTimeout(function() {
+ is(hasOrientationListeners(), true, "Handler should have been added");
+ window.ondeviceorientation = null;
+ window.setTimeout(function() {
+ is(hasOrientationListeners(), false, "Handler should have been removed");
+ resolve();
+ }, 0);
+ }, 0);
+ });
+
+ await test_event_presence("device.sensors.ambientLight.enabled", hasLightListeners, "devicelight");
+ await test_event_presence("device.sensors.proximity.enabled", hasProximityListeners, "userproximity");
+ await test_event_presence("device.sensors.motion.enabled", hasMotionListeners, "devicemotion");
+ await test_event_presence("device.sensors.orientation.enabled", hasOrientationListeners, "deviceorientation");
+
+ SimpleTest.finish();
+
+}
+
+SimpleTest.waitForExplicitFinish();
+
+start();
+
+</script>
+</pre>
+</body>
+</html>
+
diff --git a/dom/events/test/test_disabled_events.html b/dom/events/test/test_disabled_events.html
new file mode 100644
index 0000000000..c345208ee7
--- /dev/null
+++ b/dom/events/test/test_disabled_events.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1359076
+-->
+<head>
+ <title>Test for Bug 675884</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=1359076">Mozilla Bug 1359076</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [
+ ["device.sensors.orientation.enabled", false],
+ ["device.sensors.motion.enabled", false],
+ ["device.sensors.proximity.enabled", false],
+ ["device.sensors.ambientLight.enabled", false]
+]}, () => {
+ is("UserProximityEvent" in window, false, "UserProximityEvent does not exist");
+ is("DeviceLightEvent" in window, false, "DeviceLightEvent does not exist");
+ is("DeviceOrientationEvent" in window, false, "DeviceOrientationEvent does not exist");
+ is("DeviceMotionEvent" in window, false, "DeviceMotionEvent does not exist");
+ is("onuserproximity" in window, false, "onuserproximity does not exist");
+ is("ondevicelight" in window, false, "ondevicelight does not exist");
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_dnd_with_modifiers.html b/dom/events/test/test_dnd_with_modifiers.html
new file mode 100644
index 0000000000..8b861c9306
--- /dev/null
+++ b/dom/events/test/test_dnd_with_modifiers.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<html>
+ <meta charset="utf-8">
+ <title>Test dragstart, drag, dragover, drop, dragend with keyboard modifiers</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <div id="test"></div>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ SimpleTest.waitForFocus(() => {
+ let dragEvents = ["dragstart", "drag", "dragend"];
+ let dropEvents = ["dragover", "drop"];
+ let source = document.getElementById("source");
+ let target = document.getElementById("target");
+
+ dragEvents.forEach((ev, idx, array) => {
+ source.addEventListener(ev, (e) => {
+ ok(e.ctrlKey, e.type + ".ctrlKey should be true");
+ ok(!e.shiftKey, e.type + ".shiftKey should be false");
+ ok(e.altKey, e.type + ".altKey should be true");
+ }, {once: true});
+ });
+
+ dropEvents.forEach((ev, idx, array) => {
+ target.addEventListener(ev, (e) => {
+ ok(e.ctrlKey, e.type + ".ctrlKey should be true");
+ ok(!e.shiftKey, e.type + ".shiftKey should be false");
+ ok(e.altKey, e.type + ".altKey should be true");
+ }, {once: true});
+ });
+
+ source.addEventListener("dragstart", (e) => {
+ e.preventDefault();
+ }, {once: true});
+
+ source.addEventListener("dragend", (e) => {
+ SimpleTest.finish();
+ });
+
+ let selection = window.getSelection();
+ selection.selectAllChildren(source);
+
+ synthesizeMouse(source, 1, 1, {type: "mousedown", ctrlKey: true, altKey: true}, window);
+ synthesizeMouse(source, 10, 10, {type: "mousemove", ctrlKey: true, altKey: true}, window);
+ synthesizeMouse(source, 10, 10, {type: "mouseup", ctrlKey: true, altKey: true}, window);
+
+ let dragEvent = {
+ type: "drag",
+ ctrlKey: true,
+ altKey: true,
+ };
+ sendDragEvent(dragEvent, source, window);
+
+ let rect = target.getBoundingClientRect();
+ let dropEvent = {
+ ctrlKey: true,
+ altKey: true,
+ clientX: rect.left + rect.width / 2,
+ clientY: rect.top + rect.height / 2,
+ };
+ selection.selectAllChildren(source);
+ synthesizeDrop(source, target, [], "copy", window, window, dropEvent);
+
+ let dragEndEvent = {
+ type: "dragend",
+ ctrlKey: true,
+ altKey: true,
+ };
+ sendDragEvent(dragEndEvent, source, window);
+ });
+ </script>
+<body>
+ <span id="source" style="font-size: 40px;">test</span>
+ <div id="target" contenteditable="true" width="50" height="50"></div>
+</body>
+</html>
diff --git a/dom/events/test/test_dom_activate_event.html b/dom/events/test/test_dom_activate_event.html
new file mode 100644
index 0000000000..9a6d48ac64
--- /dev/null
+++ b/dom/events/test/test_dom_activate_event.html
@@ -0,0 +1,91 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test DOMActivate event</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>
+<p id="display">
+<a id="a" href="#dummy">link</a>
+<button id="button">button</button>
+<input id="checkbox" type="checkbox">
+<input id="radio" type="radio">
+<input id="submit" type="submit">
+<input id="ibutton" type="button">
+<input id="reset" type="reset">
+</p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/* eslint-disable max-nested-callbacks */
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+function runIsTrustedTestCausedByTrustedClick(aElement, aNextTest)
+{
+ const kDescription = "runIsTrustedTestCausedByTrustedClick(aElement.id=" + aElement.id + "): ";
+ var DOMActivateFired = false;
+ aElement.addEventListener("DOMActivate", function (aEvent) {
+ DOMActivateFired = true;
+ ok(aEvent.isTrusted, kDescription + "DOMActivate event should be trusted event");
+ aElement.removeEventListener("DOMActivate", arguments.callee);
+ aNextTest();
+ });
+ aElement.addEventListener("click", function (aEvent) {
+ ok(aEvent.isTrusted, kDescription + "click event should be trusted event");
+ aElement.removeEventListener("click", arguments.callee);
+ });
+ synthesizeMouseAtCenter(aElement, {});
+}
+
+function runIsTrustedTestCausedByUntrustedClick(aElement, aNextTest)
+{
+ const kDescription = "runIsTrustedTestCausedByUntrustedClick(aElement.id=" + aElement.id + "): ";
+ var DOMActivateFired = false;
+ aElement.addEventListener("DOMActivate", function (aEvent) {
+ DOMActivateFired = true;
+ ok(aEvent.isTrusted,
+ kDescription + "DOMActivate event should be trusted event even if it's caused by untrusted event");
+ aElement.removeEventListener("DOMActivate", arguments.callee);
+ aNextTest();
+ });
+ aElement.addEventListener("click", function (aEvent) {
+ ok(!aEvent.isTrusted, kDescription + "click event should be untrusted event");
+ aElement.removeEventListener("click", arguments.callee);
+ });
+ var click = new MouseEvent("click", { button: 0 });
+ aElement.dispatchEvent(click);
+}
+
+function runTests()
+{
+ // XXX Don't add indentation here. If you add indentation, the diff will be
+ // complicated when somebody adds new tests.
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("a"), function () {
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("button"), function () {
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("checkbox"), function () {
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("radio"), function () {
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("submit"), function () {
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("ibutton"), function () {
+ runIsTrustedTestCausedByTrustedClick(document.getElementById("reset"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("a"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("button"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("checkbox"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("radio"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("submit"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("ibutton"), function () {
+ runIsTrustedTestCausedByUntrustedClick(document.getElementById("reset"), function () {
+ SimpleTest.finish();
+ });});});});});});});});});});});});});});
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_dom_keyboard_event.html b/dom/events/test/test_dom_keyboard_event.html
new file mode 100644
index 0000000000..dd7b0c001a
--- /dev/null
+++ b/dom/events/test/test_dom_keyboard_event.html
@@ -0,0 +1,541 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM KeyboardEvent</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>
+<p id="display"></p>
+<p><input type="text" id="input"></p>
+<p><input type="text" id="input_readonly" readonly></p>
+<p><textarea id="textarea"></textarea></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests, window);
+
+function testInitializingUntrustedEvent()
+{
+ const kTests = [
+ // initKeyEvent
+ { createEventArg: "KeyboardEvent", useInitKeyboardEvent: false,
+ type: "keydown", bubbles: true, cancelable: true, view: null,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ keyCode: 0x00, charCode: 0x00, detail: 0, key: "", location: 0,
+ }, // 0
+
+ { createEventArg: "keyboardevent", useInitKeyboardEvent: false,
+ type: "keyup", bubbles: false, cancelable: true, view: window,
+ ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
+ keyCode: 0x10, charCode: 0x00, detail: 0, key: "", location: 0,
+ }, // 1
+
+ { createEventArg: "Keyboardevent", useInitKeyboardEvent: false,
+ type: "keypress", bubbles: true, cancelable: false, view: null,
+ ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
+ keyCode: 0x11, charCode: 0x30, detail: 0, key: "", location: 0,
+ }, // 2
+
+ { createEventArg: "keyboardEvent", useInitKeyboardEvent: false,
+ type: "boo", bubbles: false, cancelable: false, view: window,
+ ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
+ keyCode: 0x30, charCode: 0x40, detail: 0, key: "", location: 0,
+ }, // 3
+
+ { createEventArg: "KeyBoardEvent", useInitKeyboardEvent: false,
+ type: "foo", bubbles: true, cancelable: true, view: null,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
+ keyCode: 0x00, charCode: 0x50, detail: 0, key: "", location: 0,
+ }, // 4
+
+ { createEventArg: "keyboardevEnt", useInitKeyboardEvent: false,
+ type: "bar", bubbles: false, cancelable: true, view: window,
+ ctrlKey: true, altKey: true, shiftKey: false, metaKey: false,
+ keyCode: 0x00, charCode: 0x60, detail: 0, key: "", location: 0,
+ }, // 5
+
+ { createEventArg: "KeyboaRdevent", useInitKeyboardEvent: false,
+ type: "keydown", bubbles: true, cancelable: false, view: null,
+ ctrlKey: false, altKey: true, shiftKey: false, metaKey: true,
+ keyCode: 0x30, charCode: 0x00, detail: 0, key: "", location: 0,
+ }, // 6
+
+ { createEventArg: "KEYBOARDEVENT", useInitKeyboardEvent: false,
+ type: "keyup", bubbles: false, cancelable: false, view: window,
+ ctrlKey: true, altKey: false, shiftKey: true, metaKey: false,
+ keyCode: 0x10, charCode: 0x80, detail: 0, key: "", location: 0,
+ }, // 7
+
+ { createEventArg: "KeyboardEvent", useInitKeyboardEvent: false,
+ type: "keypress", bubbles: false, cancelable: false, view: window,
+ ctrlKey: true, altKey: false, shiftKey: true, metaKey: true,
+ keyCode: 0x10, charCode: 0x80, detail: 0, key: "", location: 0,
+ }, // 8
+
+ { createEventArg: "KeyboardEvent", useInitKeyboardEvent: false,
+ type: "foo", bubbles: false, cancelable: false, view: window,
+ ctrlKey: true, altKey: true, shiftKey: true, metaKey: true,
+ keyCode: 0x10, charCode: 0x80, detail: 0, key: "", location: 0,
+ }, // 9
+
+ // initKeyboardEvent
+ { createEventArg: "KeyboardEvent", useInitKeyboardEvent: true,
+ type: "keydown", bubbles: true, cancelable: true, view: null,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ keyCode: 0x00, charCode: 0x00, key: "", location: 0,
+ }, // 10
+
+ { createEventArg: "keyboardevent", useInitKeyboardEvent: true,
+ type: "keyup", bubbles: false, cancelable: true, view: window,
+ ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
+ keyCode: 0x00, charCode: 0x00, key: "Unidentified", location: 1,
+ }, // 11
+
+ { createEventArg: "Keyboardevent", useInitKeyboardEvent: true,
+ type: "keypress", bubbles: true, cancelable: false, view: null,
+ ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
+ keyCode: 0x00, charCode: 0x00, key: "FooBar", location: 2,
+ }, // 12
+
+ { createEventArg: "keyboardevent", useInitKeyboardEvent: true,
+ type: "foo", bubbles: true, cancelable: true, view: null,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
+ keyCode: 0x00, charCode: 0x00, key: "a", location: 0,
+ }, // 13
+
+ { createEventArg: "KeyBoardEvent", useInitKeyboardEvent: true,
+ type: "", bubbles: false, cancelable: false, view: null,
+ ctrlKey: true, altKey: true, shiftKey: true, metaKey: true,
+ keyCode: 0x00, charCode: 0x00, key: "3", location: 0,
+ }, // 14
+
+ { createEventArg: "keyboardevEnt", useInitKeyboardEvent: true,
+ type: "", bubbles: false, cancelable: false, view: null,
+ ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
+ keyCode: 0x00, charCode: 0x00, key: "3", location: 6,
+ }, // 15
+
+ { createEventArg: "KeyboaRdevent", useInitKeyboardEvent: true,
+ type: "", bubbles: false, cancelable: false, view: null,
+ ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
+ keyCode: 0x00, charCode: 0x00, key: "", location: 4,
+ }, // 16
+ ];
+
+ const kOtherModifierName = [
+ "CapsLock", "NumLock", "ScrollLock", "Symbol", "SymbolLock", "Fn", "FnLock", "AltGraph"
+ ];
+
+ const kInvalidModifierName = [
+ "shift", "control", "alt", "meta", "capslock", "numlock", "scrolllock",
+ "symbollock", "fn", "OS", "altgraph", "Invalid", "Shift Control",
+ "Win", "Scroll"
+ ];
+
+ const kIsInitKeyEventAvailable =
+ SpecialPowers.getBoolPref("dom.keyboardevent.init_key_event.enabled", false);
+
+ for (var i = 0; i < kTests.length; i++) {
+ var description = "testInitializingUntrustedEvent, Index: " + i + ", ";
+ const kTest = kTests[i];
+ var e = document.createEvent(kTest.createEventArg);
+ if (kTest.useInitKeyboardEvent) {
+ e.initKeyboardEvent(kTest.type, kTest.bubbles, kTest.cancelable,
+ kTest.view, kTest.key, kTest.location,
+ kTest.ctrlKey, kTest.altKey, kTest.shiftKey,
+ kTest.metaKey);
+ } else {
+ if (!kIsInitKeyEventAvailable) {
+ continue;
+ }
+ e.initKeyEvent(kTest.type, kTest.bubbles, kTest.cancelable, kTest.view,
+ kTest.ctrlKey, kTest.altKey, kTest.shiftKey, kTest.metaKey,
+ kTest.keyCode, kTest.charCode);
+ }
+ is(e.toString(), "[object KeyboardEvent]",
+ description + 'class string should be "KeyboardEvent"');
+
+ for (var attr in kTest) {
+ if (attr == "createEventArg" || attr == "useInitKeyboardEvent" || attr == "modifiersList") {
+ continue;
+ }
+ if (!kTest.useInitKeyboardEvent && attr == "keyCode") {
+ // If this is keydown, keyup of keypress event, keycod must be correct.
+ if (kTest.type == "keydown" || kTest.type == "keyup" || kTest.type == "keypress") {
+ is(e[attr], kTest[attr], description + attr + " returns wrong value");
+ // Otherwise, should be always zero (why?)
+ } else {
+ is(e[attr], 0, description + attr + " returns non-zero for invalid event");
+ }
+ } else if (!kTest.useInitKeyboardEvent && attr == "charCode") {
+ // If this is keydown or keyup event, charCode always 0.
+ if (kTest.type == "keydown" || kTest.type == "keyup") {
+ is(e[attr], 0, description + attr + " returns non-zero for keydown or keyup event");
+ // If this is keypress event, charCode must be correct.
+ } else if (kTest.type == "keypress") {
+ is(e[attr], kTest[attr], description + attr + " returns wrong value");
+ // Otherwise, we have a bug.
+ } else {
+ if (e[attr] != kTest[attr]) { // avoid random unexpected pass.
+ todo_is(e[attr], kTest[attr], description + attr + " returns wrong value");
+ }
+ }
+ } else {
+ is(e[attr], kTest[attr], description + attr + " returns wrong value");
+ }
+ }
+ is(e.isTrusted, false, description + "isTrusted returns wrong value");
+
+ // getModifierState() tests
+ is(e.getModifierState("Shift"), kTest.shiftKey,
+ description + "getModifierState(\"Shift\") returns wrong value");
+ is(e.getModifierState("Control"), kTest.ctrlKey,
+ description + "getModifierState(\"Control\") returns wrong value");
+ is(e.getModifierState("Alt"), kTest.altKey,
+ description + "getModifierState(\"Alt\") returns wrong value");
+ is(e.getModifierState("Meta"), kTest.metaKey,
+ description + "getModifierState(\"Meta\") returns wrong value");
+
+ for (var j = 0; j < kOtherModifierName.length; j++) {
+ ok(!e.getModifierState(kOtherModifierName[j]),
+ description + "getModifierState(\"" + kOtherModifierName[j] + "\") returns wrong value");
+ }
+ for (var k = 0; k < kInvalidModifierName.length; k++) {
+ ok(!e.getModifierState(kInvalidModifierName[k]),
+ description + "getModifierState(\"" + kInvalidModifierName[k] + "\") returns wrong value");
+ }
+ }
+}
+
+function testSynthesizedKeyLocation()
+{
+ const kTests = [
+ { key: "a", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_A,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "KEY_Shift", isModifier: true, isPrintable: false,
+ event: { shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_SHIFT,
+ location: KeyboardEvent.DOM_KEY_LOCATION_LEFT },
+ },
+ { key: "KEY_Shift", isModifier: true, isPrintable: false,
+ event: { shiftKey: true, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_SHIFT,
+ location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT },
+ },
+ { key: "KEY_Control", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_CONTROL,
+ location: KeyboardEvent.DOM_KEY_LOCATION_LEFT },
+ },
+ { key: "KEY_Control", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: true, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_CONTROL,
+ location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT },
+ },
+/* XXX Alt key activates menubar even if we consume the key events.
+ { key: "KEY_Alt", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_ALT,
+ location: KeyboardEvent.DOM_KEY_LOCATION_LEFT },
+ },
+ { key: "KEY_Alt", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: true, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_ALT,
+ location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT },
+ },
+*/
+ { key: "KEY_Meta", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: true, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_META,
+ location: KeyboardEvent.DOM_KEY_LOCATION_LEFT },
+ },
+ { key: "KEY_Meta", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: true, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_META,
+ location: KeyboardEvent.DOM_KEY_LOCATION_RIGHT },
+ },
+ { key: "KEY_ArrowDown", isModifier: false, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_DOWN,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "KEY_ArrowDown", isModifier: false, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_DOWN,
+ location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
+ },
+ { key: "5", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_5,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "5", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: true,
+ keyCode: KeyboardEvent.DOM_VK_NUMPAD5,
+ location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
+ },
+ { key: "+", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_EQUALS,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "+", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: true,
+ keyCode: KeyboardEvent.DOM_VK_ADD,
+ location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
+ },
+ { key: "KEY_Enter", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_RETURN,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "KEY_Enter", isModifier: false, isPrintable: true,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_RETURN,
+ location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
+ },
+ { key: "KEY_NumLock", isModifier: true, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_NUM_LOCK,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "KEY_Insert", isModifier: false, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_INSERT,
+ location: KeyboardEvent.DOM_KEY_LOCATION_STANDARD },
+ },
+ { key: "KEY_Insert", isModifier: false, isPrintable: false,
+ event: { shiftKey: false, ctrlKey: false, altKey: false, metaKey: false, numLockKey: false,
+ keyCode: KeyboardEvent.DOM_VK_INSERT,
+ location: KeyboardEvent.DOM_KEY_LOCATION_NUMPAD },
+ },
+ ];
+
+ function getLocationName(aLocation)
+ {
+ switch (aLocation) {
+ case KeyboardEvent.DOM_KEY_LOCATION_STANDARD:
+ return "DOM_KEY_LOCATION_STANDARD";
+ case KeyboardEvent.DOM_KEY_LOCATION_LEFT:
+ return "DOM_KEY_LOCATION_LEFT";
+ case KeyboardEvent.DOM_KEY_LOCATION_RIGHT:
+ return "DOM_KEY_LOCATION_RIGHT";
+ case KeyboardEvent.DOM_KEY_LOCATION_NUMPAD:
+ return "DOM_KEY_LOCATION_NUMPAD";
+ default:
+ return "Invalid value (" + aLocation + ")";
+ }
+ }
+
+ var currentTest, description;
+ var events = { keydown: false, keypress: false, keyup: false };
+
+ function handler(aEvent)
+ {
+ is(aEvent.location, currentTest.event.location,
+ description + "location of " + aEvent.type + " was invalid");
+ events[aEvent.type] = true;
+ if (aEvent.type != "keydown" ||
+ (currentTest.event.isModifier && aEvent.type == "keydown")) {
+ aEvent.preventDefault();
+ }
+ }
+
+ window.addEventListener("keydown", handler, true);
+ window.addEventListener("keypress", handler, true);
+ window.addEventListener("keyup", handler, true);
+
+ for (var i = 0; i < kTests.length; i++) {
+ currentTest = kTests[i];
+ events = { keydown: false, keypress: false, keyup: false };
+ description = "testSynthesizedKeyLocation, " + i + ", key: " +
+ currentTest.key + ", location: " +
+ getLocationName(currentTest.event.location) + ": ";
+ synthesizeKey(currentTest.key, currentTest.event);
+ ok(events.keydown, description + "keydown event wasn't fired");
+ if (currentTest.isPrintable) {
+ ok(events.keypress, description + "keypress event wasn't fired for printable key");
+ } else {
+ ok(!events.keypress, description + "keypress event was fired for non-printable key");
+ }
+ ok(events.keyup, description + "keyup event wasn't fired");
+ }
+
+ window.removeEventListener("keydown", handler, true);
+ window.removeEventListener("keypress", handler, true);
+ window.removeEventListener("keyup", handler, true);
+}
+
+// We're using TextEventDispatcher to decide if we should keypress event
+// on content in the default event group. So, we can test if keypress
+// event is NOT fired unexpectedly with synthesizeKey().
+function testEnterKeyPressEvent()
+{
+ let keydownFired, keypressFired, beforeinputFired;
+ function onEvent(aEvent) {
+ switch (aEvent.type) {
+ case "keydown":
+ keydownFired = true;
+ break;
+ case "keypress":
+ keypressFired = true;
+ break;
+ case "beforeinput":
+ beforeinputFired = true;
+ break;
+ }
+ }
+
+ for (let targetId of ["input", "textarea", "input_readonly"]) {
+ let target = document.getElementById(targetId);
+
+ function reset() {
+ keydownFired = keypressFired = beforeinputFired = false;
+ target.value = "";
+ }
+
+ target.addEventListener("keydown", onEvent);
+ target.addEventListener("keypress", onEvent);
+ target.addEventListener("beforeinput", onEvent);
+
+ const kDescription = "<" + targetId.replace("_", " ") + ">: ";
+ let isEditable = !kDescription.includes("readonly");
+ let isTextarea = kDescription.includes("textarea");
+
+ target.focus();
+
+ reset();
+ synthesizeKey("KEY_Enter");
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Enter key is pressed");
+ is(keypressFired, true,
+ kDescription + "keypress event should be fired when Enter key is pressed");
+ if (isEditable) {
+ ok(beforeinputFired,
+ kDescription + "beforeinput event should be fired (if it's enabled) when Enter key is pressed");
+ } else {
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Enter key is pressed");
+ }
+ if (isTextarea) {
+ is(target.value, "\n",
+ kDescription + "Enter key should cause inputting a line break in <textarea>");
+ } else {
+ is(target.value, "",
+ kDescription + "Enter key should not cause inputting a line break");
+ }
+
+ reset();
+ synthesizeKey("KEY_Enter", {shiftKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Shift + Enter key is pressed");
+ is(keypressFired, true,
+ kDescription + "keypress event should be fired when Shift + Enter key is pressed");
+ if (isEditable) {
+ ok(beforeinputFired,
+ kDescription + "beforeinput event should be fired (if it's enabled) when Shift + Enter key is pressed");
+ } else {
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Shift + Enter key is pressed");
+ }
+ if (isTextarea) {
+ is(target.value, "\n",
+ kDescription + "Shift + Enter key should cause inputting a line break in <textarea>");
+ } else {
+ is(target.value, "",
+ kDescription + "Shift + Enter key should not cause inputting a line break");
+ }
+
+ reset();
+ synthesizeKey("KEY_Enter", {ctrlKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Ctrl + Enter key is pressed");
+ is(keypressFired, true,
+ kDescription + "keypress event should be fired when Ctrl + Enter key is pressed");
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Ctrl + Enter key is pressed");
+ is(target.value, "",
+ kDescription + "Ctrl + Enter key should not cause inputting a line break");
+
+ reset();
+ synthesizeKey("KEY_Enter", {altKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Alt + Enter key is pressed");
+ ok(!keypressFired,
+ kDescription + "keypress event shouldn't be fired when Alt + Enter key is pressed in strict keypress dispatching mode");
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Alt + Enter key is pressed");
+ is(target.value, "",
+ kDescription + "Alt + Enter key should not cause inputting a line break");
+
+ reset();
+ synthesizeKey("KEY_Enter", {metaKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Meta + Enter key is pressed");
+ ok(!keypressFired,
+ kDescription + "keypress event shouldn't be fired when Meta + Enter key is pressed in strict keypress dispatching mode");
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Meta + Enter key is pressed");
+ is(target.value, "",
+ kDescription + "Meta + Enter key should not cause inputting a line break");
+
+ reset();
+ synthesizeKey("KEY_Enter", {shiftKey: true, ctrlKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Ctrl + Shift + Enter key is pressed");
+ ok(!keypressFired,
+ kDescription + "keypress event shouldn't be fired when Ctrl + Shift + Enter key is pressed in strict keypress dispatching mode");
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Ctrl + Shift + Enter key is pressed");
+ is(target.value, "",
+ kDescription + "Ctrl + Shift + Enter key should not cause inputting a line break");
+
+ reset();
+ synthesizeKey("KEY_Enter", {shiftKey: true, altKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Alt + Shift + Enter key is pressed");
+ ok(!keypressFired,
+ kDescription + "keypress event shouldn't be fired when Alt + Shift + Enter key is pressed in strict keypress dispatching mode");
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Alt + Shift + Enter key is pressed");
+ is(target.value, "",
+ kDescription + "Alt + Shift + Enter key should not cause inputting a line break");
+
+ reset();
+ synthesizeKey("KEY_Enter", {shiftKey: true, metaKey: true});
+ is(keydownFired, true,
+ kDescription + "keydown event should be fired when Meta + Shift + Enter key is pressed");
+ ok(!keypressFired,
+ kDescription + "keypress event shouldn't be fired when Meta + Shift + Enter key is pressed in strict keypress dispatching mode");
+ is(beforeinputFired, false,
+ kDescription + "beforeinput event shouldn't be fired when Meta + Shift + Enter key is pressed");
+ is(target.value, "",
+ kDescription + "Meta + Shift + Enter key should not cause inputting a line break");
+
+ target.removeEventListener("keydown", onEvent);
+ target.removeEventListener("keypress", onEvent);
+ target.removeEventListener("beforeinput", onEvent);
+ }
+}
+
+function runTests()
+{
+ testInitializingUntrustedEvent();
+ testSynthesizedKeyLocation();
+ testEnterKeyPressEvent();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_dom_mouse_event.html b/dom/events/test/test_dom_mouse_event.html
new file mode 100644
index 0000000000..a441876e1f
--- /dev/null
+++ b/dom/events/test/test_dom_mouse_event.html
@@ -0,0 +1,143 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM MouseEvent</title>
+ <script src="/tests/SimpleTest/SimpleTest.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>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests, window);
+
+function testInitializingUntrustedEvent()
+{
+ const kTests = [
+ { createEventArg: "MouseEvent",
+ type: "mousedown", bubbles: true, cancelable: true, view: null, detail: 1,
+ screenX: 0, screenY: 0, clientX: 0, clientY: 0,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: false,
+ button: 6, relatedTarget: null },
+
+ { createEventArg: "mouseevent",
+ type: "mouseup", bubbles: false, cancelable: true, view: window, detail: 2,
+ screenX: 0, screenY: 0, clientX: 0, clientY: 400,
+ ctrlKey: true, altKey: false, shiftKey: false, metaKey: false,
+ button: 1, relatedTarget: document.getElementById("content") },
+
+ { createEventArg: "Mouseevent",
+ type: "click", bubbles: true, cancelable: false, view: null, detail: -5,
+ screenX: 0, screenY: 0, clientX: 300, clientY: 0,
+ ctrlKey: false, altKey: true, shiftKey: false, metaKey: false,
+ button: 2, relatedTarget: document.getElementById("test") },
+
+ { createEventArg: "mouseEvent",
+ type: "dblclick", bubbles: false, cancelable: false, view: window, detail: -1,
+ screenX: 0, screenY: 200, clientX: 0, clientY: 0,
+ ctrlKey: false, altKey: false, shiftKey: true, metaKey: false,
+ button: 12, relatedTarget: document.getElementById("content") },
+
+ { createEventArg: "MouseEvents",
+ type: "mouseenter", bubbles: true, cancelable: true, view: null, detail: 111000,
+ screenX: 100, screenY: 0, clientX: 0, clientY: 0,
+ ctrlKey: false, altKey: false, shiftKey: false, metaKey: true,
+ button: 2, relatedTarget: document.getElementById("test") },
+
+ { createEventArg: "mouseevents",
+ type: "mouseleave", bubbles: false, cancelable: true, view: window, detail: 500,
+ screenX: 100, screenY: 500, clientX: 0, clientY: 0,
+ ctrlKey: true, altKey: true, shiftKey: false, metaKey: false,
+ button: 8, relatedTarget: document.getElementById("content") },
+
+ { createEventArg: "Mouseevents",
+ type: "mouseover", bubbles: true, cancelable: false, view: null, detail: 3,
+ screenX: 0, screenY: 0, clientX: 200, clientY: 300,
+ ctrlKey: false, altKey: true, shiftKey: false, metaKey: true,
+ button: 7, relatedTarget: document.getElementById("test") },
+
+ { createEventArg: "mouseEvents",
+ type: "mouseout", bubbles: false, cancelable: false, view: window, detail: 5,
+ screenX: -100, screenY: 300, clientX: 600, clientY: -500,
+ ctrlKey: true, altKey: false, shiftKey: true, metaKey: false,
+ button: 8, relatedTarget: document.getElementById("content") },
+
+ { createEventArg: "MouseEvent",
+ type: "mousemove", bubbles: false, cancelable: false, view: window, detail: 30,
+ screenX: 500, screenY: -100, clientX: -8888, clientY: -5000,
+ ctrlKey: true, altKey: false, shiftKey: true, metaKey: true,
+ button: 8, relatedTarget: document.getElementById("test") },
+
+ { createEventArg: "MouseEvent",
+ type: "foo", bubbles: false, cancelable: false, view: window, detail: 100,
+ screenX: 2000, screenY: 6000, clientX: 5000, clientY: 3000,
+ ctrlKey: true, altKey: true, shiftKey: true, metaKey: true,
+ button: 8, relatedTarget: document.getElementById("test") },
+ ];
+
+ const kOtherModifierName = [
+ "CapsLock", "NumLock", "ScrollLock", "Symbol", "SymbolLock", "Fn", "FnLock", "AltGraph"
+ ];
+
+ const kInvalidModifierName = [
+ "shift", "control", "alt", "meta", "capslock", "numlock", "scrolllock",
+ "symbollock", "fn", "OS", "altgraph", "Invalid", "Shift Control",
+ "Win", "Scroll"
+ ];
+
+ for (var i = 0; i < kTests.length; i++) {
+ var description = "testInitializingUntrustedEvent, Index: " + i + ", ";
+ const kTest = kTests[i];
+ var e = document.createEvent(kTest.createEventArg);
+ e.initMouseEvent(kTest.type, kTest.bubbles, kTest.cancelable, kTest.view,
+ kTest.detail, kTest.screenX, kTest.screenY, kTest.clientX, kTest.clientY,
+ kTest.ctrlKey, kTest.altKey, kTest.shiftKey, kTest.metaKey,
+ kTest.button, kTest.relatedTarget);
+
+ for (var attr in kTest) {
+ if (attr == "createEventArg") {
+ continue;
+ }
+ is(e[attr], kTest[attr], description + attr + " returns wrong value");
+ }
+ is(e.isTrusted, false, description + "isTrusted returns wrong value");
+ is(e.buttons, 0, description + "buttons returns wrong value");
+ is(e.movementX, 0, description + "movementX returns wrong value");
+ is(e.movementY, 0, description + "movementY returns wrong value");
+
+ // getModifierState() tests
+ is(e.getModifierState("Shift"), kTest.shiftKey,
+ description + "getModifierState(\"Shift\") returns wrong value");
+ is(e.getModifierState("Control"), kTest.ctrlKey,
+ description + "getModifierState(\"Control\") returns wrong value");
+ is(e.getModifierState("Alt"), kTest.altKey,
+ description + "getModifierState(\"Alt\") returns wrong value");
+ is(e.getModifierState("Meta"), kTest.metaKey,
+ description + "getModifierState(\"Meta\") returns wrong value");
+
+ for (var j = 0; j < kOtherModifierName.length; j++) {
+ ok(!e.getModifierState(kOtherModifierName[j]),
+ description + "getModifierState(\"" + kOtherModifierName[j] + "\") returns wrong value");
+ }
+ for (var k = 0; k < kInvalidModifierName.length; k++) {
+ ok(!e.getModifierState(kInvalidModifierName[k]),
+ description + "getModifierState(\"" + kInvalidModifierName[k] + "\") returns wrong value");
+ }
+ }
+}
+
+function runTests()
+{
+ testInitializingUntrustedEvent();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_dom_storage_event.html b/dom/events/test/test_dom_storage_event.html
new file mode 100644
index 0000000000..88cc288d41
--- /dev/null
+++ b/dom/events/test/test_dom_storage_event.html
@@ -0,0 +1,62 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM StorageEvent</title>
+ <script src="/tests/SimpleTest/SimpleTest.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>
+<pre id="test">
+<script type="application/javascript">
+
+const kTests = [
+ { createEventArg: "StorageEvent",
+ type: "aaa", bubbles: true, cancelable: true,
+ key: null, oldValue: 'a', newValue: 'b', url: 'c', storageArea: null },
+
+ { createEventArg: "storageevent",
+ type: "bbb", bubbles: false, cancelable: true,
+ key: 'key', oldValue: null, newValue: 'b', url: 'c', storageArea: null },
+
+ { createEventArg: "Storageevent",
+ type: "ccc", bubbles: true, cancelable: false,
+ key: 'key', oldValue: 'a', newValue: null, url: 'c', storageArea: null },
+
+ { createEventArg: "storageEvent",
+ type: "ddd", bubbles: false, cancelable: false,
+ key: 'key', oldValue: 'a', newValue: 'b', url: null, storageArea: null },
+
+ { createEventArg: "StorageEvent",
+ type: "eee", bubbles: true, cancelable: true,
+ key: 'key', oldValue: 'a', newValue: 'b', url: 'c', storageArea: null },
+
+ { createEventArg: "storageevent",
+ type: "fff", bubbles: false, cancelable: true,
+ key: null, oldValue: null, newValue: null, url: null, storageArea: null },
+ ];
+
+for (var i = 0; i < kTests.length; i++) {
+ var description = "test, Index: " + i + ", ";
+ const kTest = kTests[i];
+ var e = document.createEvent(kTest.createEventArg);
+ e.initStorageEvent(kTest.type, kTest.bubbles, kTest.cancelable,
+ kTest.key, kTest.oldValue, kTest.newValue, kTest.url,
+ kTest.storageArea);
+
+ for (var attr in kTest) {
+ if (attr == 'createEventArg')
+ continue;
+
+ is(e[attr], kTest[attr], description + attr + " returns wrong value");
+ }
+ is(e.isTrusted, false, description + "isTrusted returns wrong value");
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_dom_wheel_event.html b/dom/events/test/test_dom_wheel_event.html
new file mode 100644
index 0000000000..6a18a9e4d7
--- /dev/null
+++ b/dom/events/test/test_dom_wheel_event.html
@@ -0,0 +1,835 @@
+<!DOCTYPE HTML>
+<html style="font-size: 32px;">
+<head>
+ <title>Test for D3E WheelEvent</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="scrollable" style="font-family: monospace; font-size: 16px; line-height: 1; overflow: auto; width: 200px; height: 200px;">
+ <div id="scrolled" style="font-size: 64px; width: 5000px; height: 5000px;">
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ </div>
+</div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTest, window);
+
+var gScrollableElement = document.getElementById("scrollable");
+var gScrolledElement = document.getElementById("scrolled");
+
+var gLineHeight = 0;
+var gHorizontalLine = 0;
+var gPageHeight = 0;
+var gPageWidth = 0;
+
+function sendWheelAndWait(aX, aY, aEvent)
+{
+ sendWheelAndPaint(gScrollableElement, aX, aY, aEvent, continueTest);
+}
+
+function* prepareScrollUnits()
+{
+ var result = -1;
+ function handler(aEvent)
+ {
+ result = aEvent.detail;
+ aEvent.preventDefault();
+ }
+ window.addEventListener("MozMousePixelScroll", handler, { capture: true, passive: false });
+
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gLineHeight = result;
+ ok(gLineHeight > 10 && gLineHeight < 25, "prepareScrollUnits: gLineHeight may be illegal value, got " + gLineHeight);
+
+ result = -1;
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gHorizontalLine = result;
+ ok(gHorizontalLine > 5 && gHorizontalLine < 16, "prepareScrollUnits: gHorizontalLine may be illegal value, got " + gHorizontalLine);
+
+ result = -1;
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gPageHeight = result;
+ // XXX Cannot we know the actual scroll port size?
+ ok(gPageHeight >= 150 && gPageHeight <= 200,
+ "prepareScrollUnits: gPageHeight is strange value, got " + gPageHeight);
+
+ result = -1;
+ yield sendWheelAndWait(10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gPageWidth = result;
+ ok(gPageWidth >= 150 && gPageWidth <= 200,
+ "prepareScrollUnits: gPageWidth is strange value, got " + gPageWidth);
+
+ window.removeEventListener("MozMousePixelScroll", handler, true);
+}
+
+function testMakingUntrustedEvent()
+{
+ const kCreateEventArgs = [
+ "WheelEvent", "wheelevent", "wheelEvent", "Wheelevent"
+ ];
+
+ for (var i = 0; i < kCreateEventArgs.length; i++) {
+ try {
+ // We never support WheelEvent construction with document.createEvent().
+ var event = document.createEvent(kCreateEventArgs[i]);
+ ok(false, "document.createEvent(" + kCreateEventArgs[i] + ") should throw an error");
+ } catch (e) {
+ ok(true, "document.createEvent(" + kCreateEventArgs[i] + ") threw an error");
+ }
+ }
+
+ var wheelEvent = new WheelEvent("wheel");
+ ok(wheelEvent instanceof WheelEvent,
+ "new WheelEvent() should create an instance of WheelEvent");
+ ok(typeof(wheelEvent.initWheelEvent) != "function",
+ "WheelEvent must not have initWheelEvent()");
+}
+
+// delta_multiplier prefs should cause changing delta values of trusted events only.
+// And also legacy events' detail value should be changed too.
+function* testDeltaMultiplierPrefs()
+{
+ const kModifierAlt = 0x01;
+ const kModifierControl = 0x02;
+ const kModifierMeta = 0x04;
+ const kModifierShift = 0x08;
+
+ const kTests = [
+ { name: "default",
+ expected: [ 0, kModifierShift | kModifierAlt, kModifierShift | kModifierControl,
+ kModifierShift | kModifierMeta,
+ kModifierControl | kModifierAlt, kModifierMeta | kModifierAlt ],
+ unexpected: [ kModifierAlt, kModifierControl, kModifierMeta, kModifierShift ] },
+ { name: "with_alt",
+ expected: [ kModifierAlt ],
+ unexpected: [0, kModifierControl, kModifierMeta, kModifierShift,
+ kModifierShift | kModifierAlt, kModifierControl | kModifierAlt,
+ kModifierMeta | kModifierAlt ] },
+ { name: "with_control",
+ expected: [ kModifierControl ],
+ unexpected: [0, kModifierAlt, kModifierMeta, kModifierShift,
+ kModifierShift | kModifierControl, kModifierControl | kModifierAlt,
+ kModifierMeta | kModifierControl ] },
+ { name: "with_meta",
+ expected: [ kModifierMeta ],
+ unexpected: [0, kModifierAlt, kModifierControl, kModifierShift,
+ kModifierShift | kModifierMeta, kModifierControl | kModifierMeta,
+ kModifierMeta | kModifierAlt ] },
+ { name: "with_shift",
+ expected: [ kModifierShift ],
+ unexpected: [0, kModifierAlt, kModifierControl, kModifierMeta,
+ kModifierShift | kModifierAlt, kModifierControl | kModifierShift,
+ kModifierMeta | kModifierShift ] },
+ ];
+
+ // Note that this test doesn't support complicated lineOrPageDelta values which are computed with
+ // accumulated delta values by the prefs. If you need to test the lineOrPageDelta accumulation,
+ // use test_continuous_dom_wheel_event.html.
+ const kEvents = [
+ { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: gHorizontalLine, deltaY: gLineHeight, deltaZ: gLineHeight, lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 1.0, lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -gHorizontalLine, deltaY: -gLineHeight, deltaZ: -gLineHeight, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: -1.0, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
+ { deltaMode: WheelEvent.DOM_DELTA_LINE, skipDeltaModeCheck: true,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: -1.0, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: -1.0, lineOrPageDeltaX: -1, lineOrPageDeltaY: -1 },
+ ];
+
+ const kDeltaMultiplierPrefs = [
+ "delta_multiplier_x", "delta_multiplier_y", "delta_multiplier_z"
+ ];
+
+ const kPrefValues = [
+ 200, 50, 0, -50, -150
+ ];
+
+ var currentTest, currentModifiers, currentEvent, currentPref, currentMultiplier, testingExpected;
+ var expectedAsyncHandlerCalls;
+ var description;
+ var calledHandlers = { wheel: false,
+ DOMMouseScroll: { horizontal: false, vertical: false },
+ MozMousePixelScroll: { horizontal: false, vertical: false } };
+
+ function deltaToPixels(mode, delta, horizontal) {
+ switch (mode) {
+ case WheelEvent.DOM_DELTA_PIXEL:
+ return delta;
+ case WheelEvent.DOM_DELTA_LINE:
+ return delta * (horizontal ? gHorizontalLine : gLineHeight);
+ case WheelEvent.DOM_DELTA_PAGE:
+ return delta * (horizontal ? gPageWidth : gPageHeight);
+ }
+ throw new Error("Unknown delta mode");
+ }
+
+ function defaultScrollMultiplier(horizontal) {
+ if (!SpecialPowers.getBoolPref("mousewheel.system_scroll_override.enabled")) {
+ return 1;
+ }
+ return SpecialPowers.getIntPref(`mousewheel.system_scroll_override.${horizontal ? "horizontal" : "vertical"}.factor`) / 100;
+ }
+
+ function wheelEventHandler(aEvent) {
+ calledHandlers.wheel = true;
+
+ var expectedDeltaX = currentEvent.deltaX;
+ var expectedDeltaY = currentEvent.deltaY;
+ var expectedDeltaZ = currentEvent.deltaZ;
+
+ if (testingExpected) {
+ switch (currentPref.charAt(currentPref.length - 1)) {
+ case "x":
+ expectedDeltaX *= currentMultiplier;
+ break;
+ case "y":
+ expectedDeltaY *= currentMultiplier;
+ break;
+ case "z":
+ expectedDeltaZ *= currentMultiplier;
+ break;
+ }
+ } else if (aEvent.isTrusted && aEvent.deltaMode == WheelEvent.DOM_DELTA_LINE) {
+ expectedDeltaX *= defaultScrollMultiplier(true);
+ expectedDeltaY *= defaultScrollMultiplier(false);
+ }
+ is(aEvent.deltaMode, currentEvent.deltaMode, description + "deltaMode (" + currentEvent.deltaMode + ") was invalid");
+ is(aEvent.deltaX, expectedDeltaX, description + "deltaX (" + currentEvent.deltaX + ") was invalid");
+ is(aEvent.deltaY, expectedDeltaY, description + "deltaY (" + currentEvent.deltaY + ") was invalid");
+ is(aEvent.deltaZ, expectedDeltaZ, description + "deltaZ (" + currentEvent.deltaZ + ") was invalid");
+ is(aEvent.wheelDeltaX, -Math.round(aEvent.isTrusted ? 3 * deltaToPixels(aEvent.deltaMode, expectedDeltaX, true) : expectedDeltaX), `${description}wheelDeltaX (${aEvent.wheelDeltaX}) was invalid`);
+ is(aEvent.wheelDeltaY, -Math.round(aEvent.isTrusted ? 3 * deltaToPixels(aEvent.deltaMode, expectedDeltaY, false) : expectedDeltaY), `${description}wheelDeltaY (${aEvent.wheelDeltaY}) was invalid`);
+ is(aEvent.wheelDelta, aEvent.wheelDeltaY || aEvent.wheelDeltaX, description + "wheelDelta was invalid");
+
+ if (expectedAsyncHandlerCalls > 0 && --expectedAsyncHandlerCalls == 0) {
+ setTimeout(continueTest, 0);
+ }
+ }
+
+ function legacyEventHandler(aEvent) {
+ var isHorizontal = (aEvent.axis == MouseScrollEvent.HORIZONTAL_AXIS);
+ var isScrollEvent = (aEvent.type == "DOMMouseScroll");
+ if (isScrollEvent) {
+ if (isHorizontal) {
+ calledHandlers.DOMMouseScroll.horizontal = true;
+ } else {
+ calledHandlers.DOMMouseScroll.vertical = true;
+ }
+ } else {
+ if (isHorizontal) {
+ calledHandlers.MozMousePixelScroll.horizontal = true;
+ } else {
+ calledHandlers.MozMousePixelScroll.vertical = true;
+ }
+ }
+ var eventName = (isHorizontal ? "Horizontal " : "Vertical ") + aEvent.type + " ";
+ var expectedDetail;
+ if (isScrollEvent) {
+ expectedDetail = isHorizontal ? currentEvent.lineOrPageDeltaX : currentEvent.lineOrPageDeltaY;
+ if (currentEvent.deltaMode == WheelEvent.DOM_DELTA_PAGE && expectedDetail) {
+ expectedDetail = ((expectedDetail > 0) ? UIEvent.SCROLL_PAGE_DOWN : UIEvent.SCROLL_PAGE_UP);
+ }
+ } else {
+ expectedDetail = isHorizontal ? currentEvent.deltaX : currentEvent.deltaY;
+ if (expectedDetail) {
+ if (currentEvent.deltaMode == WheelEvent.DOM_DELTA_LINE) {
+ expectedDetail *= (isHorizontal ? gHorizontalLine : gLineHeight);
+ } else if (currentEvent.deltaMode == WheelEvent.DOM_DELTA_PAGE) {
+ if (expectedDetail > 0) {
+ expectedDetail = (isHorizontal ? gPageWidth : gPageHeight);
+ } else {
+ expectedDetail = (isHorizontal ? -gPageWidth : -gPageHeight);
+ }
+ }
+ }
+ }
+ if (testingExpected) {
+ if ((isHorizontal && currentPref.charAt(currentPref.length - 1) == "x") ||
+ (!isHorizontal && currentPref.charAt(currentPref.length - 1) == "y")) {
+ // If it's a page scroll event, the detail value is UIEvent.SCROLL_PAGE_DOWN or
+ // UIEvent.SCROLL_PAGE_UP. If the delta value sign is reverted, we need to
+ // revert the expected detail value too. Otherwise, don't touch it.
+ if (isScrollEvent && currentEvent.deltaMode == WheelEvent.DOM_DELTA_PAGE) {
+ if (currentMultiplier < 0) {
+ expectedDetail = ((expectedDetail == UIEvent.SCROLL_PAGE_UP) ? UIEvent.SCROLL_PAGE_DOWN : UIEvent.SCROLL_PAGE_UP);
+ }
+ } else {
+ expectedDetail *= currentMultiplier;
+ expectedDetail = expectedDetail < 0 ? Math.ceil(expectedDetail) : Math.floor(expectedDetail);
+ }
+ }
+ }
+ is(aEvent.detail, expectedDetail, description + eventName + "detail was invalid");
+
+ aEvent.preventDefault();
+
+ if (expectedAsyncHandlerCalls > 0 && --expectedAsyncHandlerCalls == 0) {
+ setTimeout(continueTest, 0);
+ }
+ }
+
+ window.addEventListener("wheel", wheelEventHandler, { capture: true, passive: false });
+ window.addEventListener("DOMMouseScroll", legacyEventHandler, { capture: true, passive: false });
+ window.addEventListener("MozMousePixelScroll", legacyEventHandler, { capture: true, passive: false });
+
+ function* dispatchEvent(aIsExpected) {
+ for (var i = 0; i < kEvents.length; i++) {
+ currentEvent = kEvents[i];
+ currentEvent.shiftKey = (currentModifiers & kModifierShift) != 0;
+ currentEvent.ctrlKey = (currentModifiers & kModifierControl) != 0;
+ currentEvent.altKey = (currentModifiers & kModifierAlt) != 0;
+ currentEvent.metaKey = (currentModifiers & kModifierMeta) != 0;
+ var modifierList = "";
+ if (currentEvent.shiftKey) {
+ modifierList += "Shift ";
+ }
+ if (currentEvent.ctrlKey) {
+ modifierList += "Control ";
+ }
+ if (currentEvent.altKey) {
+ modifierList += "Alt ";
+ }
+ if (currentEvent.metaKey) {
+ modifierList += "Meta ";
+ }
+
+ for (var j = 0; j < kPrefValues.length; j++) {
+ currentMultiplier = kPrefValues[j] / 100;
+ for (var k = 0; k < kDeltaMultiplierPrefs.length; k++) {
+ currentPref = "mousewheel." + currentTest.name + "." + kDeltaMultiplierPrefs[k];
+
+ yield SpecialPowers.pushPrefEnv({"set": [[currentPref, kPrefValues[j]]]}, continueTest);
+
+ gScrollableElement.scrollTop = gScrollableElement.scrollBottom = 1000;
+
+ // trusted event's delta valuses should be reverted by the pref.
+ testingExpected = aIsExpected;
+
+ var expectedProps = {
+ deltaX: currentEvent.deltaX * currentMultiplier,
+ deltaY: currentEvent.deltaY * currentMultiplier,
+ dletaZ: currentEvent.deltaZ * currentMultiplier,
+ lineOrPageDeltaX: currentEvent.lineOrPageDeltaX * currentMultiplier,
+ lineOrPageDeltaY: currentEvent.lineOrPageDeltaY * currentMultiplier,
+ };
+
+ var expectedWheel = expectedProps.deltaX != 0 || expectedProps.deltaY != 0 || expectedProps.deltaZ != 0;
+ var expectedDOMMouseX = expectedProps.lineOrPageDeltaX >= 1 || expectedProps.lineOrPageDeltaX <= -1;
+ var expectedDOMMouseY = expectedProps.lineOrPageDeltaY >= 1 || expectedProps.lineOrPageDeltaY <= -1;
+ var expectedMozMouseX = expectedProps.deltaX >= 1 || expectedProps.deltaX <= -1;
+ var expectedMozMouseY = expectedProps.deltaY >= 1 || expectedProps.deltaY <= -1;
+
+ expectedAsyncHandlerCalls = 0;
+ if (expectedWheel) ++expectedAsyncHandlerCalls;
+ if (expectedDOMMouseX) ++expectedAsyncHandlerCalls;
+ if (expectedDOMMouseY) ++expectedAsyncHandlerCalls;
+ if (expectedMozMouseX) ++expectedAsyncHandlerCalls;
+ if (expectedMozMouseY) ++expectedAsyncHandlerCalls;
+
+ description = "testDeltaMultiplierPrefs, pref: " + currentPref + "=" + kPrefValues[j] +
+ ", deltaMode: " + currentEvent.deltaMode + ", modifiers: \"" + modifierList + "\", (trusted event): ";
+ yield synthesizeWheel(gScrollableElement, 10, 10, currentEvent);
+
+ is(calledHandlers.wheel,
+ expectedWheel,
+ description + "wheel event was (not) fired");
+ is(calledHandlers.DOMMouseScroll.horizontal,
+ expectedDOMMouseX,
+ description + "Horizontal DOMMouseScroll event was (not) fired");
+ is(calledHandlers.DOMMouseScroll.vertical,
+ expectedDOMMouseY,
+ description + "Vertical DOMMouseScroll event was (not) fired");
+ is(calledHandlers.MozMousePixelScroll.horizontal,
+ expectedMozMouseX,
+ description + "Horizontal MozMousePixelScroll event was (not) fired");
+ is(calledHandlers.MozMousePixelScroll.vertical,
+ expectedMozMouseY,
+ description + "Vertical MozMousePixelScroll event was (not) fired");
+
+ calledHandlers = { wheel: false,
+ DOMMouseScroll: { horizontal: false, vertical: false },
+ MozMousePixelScroll: { horizontal: false, vertical: false } };
+
+ // untrusted event's delta values shouldn't be reverted by the pref.
+ testingExpected = false;
+ var props = {
+ bubbles: true,
+ cancelable: true,
+ shiftKey: currentEvent.shiftKey,
+ ctrlKey: currentEvent.ctrlKey,
+ altKey: currentEvent.altKey,
+ metaKey: currentEvent.metaKey,
+ deltaX: currentEvent.deltaX,
+ deltaY: currentEvent.deltaY,
+ deltaZ: currentEvent.deltaZ,
+ deltaMode: currentEvent.deltaMode,
+ };
+ var untrustedEvent = new WheelEvent("wheel", props);
+
+ description = "testDeltaMultiplierPrefs, pref: " + currentPref + "=" + kPrefValues[j] +
+ ", deltaMode: " + currentEvent.deltaMode + ", modifiers: \"" + modifierList + "\", (untrusted event): ";
+ gScrollableElement.dispatchEvent(untrustedEvent);
+
+ ok(calledHandlers.wheel, description + "wheel event was not fired for untrusted event");
+ ok(!calledHandlers.DOMMouseScroll.horizontal,
+ description + "Horizontal DOMMouseScroll event was fired for untrusted event");
+ ok(!calledHandlers.DOMMouseScroll.vertical,
+ description + "Vertical DOMMouseScroll event was fired for untrusted event");
+ ok(!calledHandlers.MozMousePixelScroll.horizontal,
+ description + "Horizontal MozMousePixelScroll event was fired for untrusted event");
+ ok(!calledHandlers.MozMousePixelScroll.vertical,
+ description + "Vertical MozMousePixelScroll event was fired for untrusted event");
+
+ yield SpecialPowers.pushPrefEnv({"set": [[currentPref, 100]]}, continueTest);
+
+ calledHandlers = { wheel: false,
+ DOMMouseScroll: { horizontal: false, vertical: false },
+ MozMousePixelScroll: { horizontal: false, vertical: false } };
+
+ }
+ // We should skip other value tests if testing with modifier key.
+ // If we didn't do so, it would test too many times, but we don't need to do so.
+ if (kTests.name != "default") {
+ break;
+ }
+ }
+ }
+ }
+
+ for (var i = 0; i < kTests.length; i++) {
+ currentTest = kTests[i];
+ for (var j = 0; j < currentTest.expected.length; j++) {
+ currentModifiers = currentTest.expected[j];
+ yield* dispatchEvent(true);
+ }
+ for (var k = 0; k < currentTest.unexpected.length; k++) {
+ currentModifiers = currentTest.unexpected[k];
+ yield* dispatchEvent(false);
+ }
+ }
+
+ window.removeEventListener("wheel", wheelEventHandler, true);
+ window.removeEventListener("DOMMouseScroll", legacyEventHandler, true);
+ window.removeEventListener("MozMousePixelScroll", legacyEventHandler, true);
+}
+
+// Untrusted wheel events shouldn't cause legacy mouse scroll events.
+function testDispatchingUntrustEvent()
+{
+ var descriptionBase = "testDispatchingUntrustEvent, ";
+ var description, wheelEventFired;
+ function wheelEventHandler(aEvent)
+ {
+ wheelEventFired = true;
+ }
+
+ function legacyEventHandler(aEvent)
+ {
+ ok(false, aEvent.type + " must not be fired");
+ }
+
+ window.addEventListener("wheel", wheelEventHandler, true);
+ window.addEventListener("DOMMouseScroll", legacyEventHandler, true);
+ window.addEventListener("MozMousePixelScroll", legacyEventHandler, true);
+
+ description = descriptionBase + "dispatching a pixel wheel event: ";
+ wheelEventFired = false;
+ var untrustedPixelEvent = new WheelEvent("wheel", {
+ bubbles: true, cancelable: true,
+ deltaX: 24.0, deltaY: 24.0,
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ });
+ gScrolledElement.dispatchEvent(untrustedPixelEvent);
+ ok(wheelEventFired, description + "wheel event wasn't fired");
+
+ description = descriptionBase + "dispatching a line wheel event: ";
+ wheelEventFired = false;
+ var untrustedLineEvent = new WheelEvent("wheel", {
+ bubbles: true, cancelable: true,
+ deltaX: 3.0, deltaY: 3.0,
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ });
+ gScrolledElement.dispatchEvent(untrustedLineEvent);
+ ok(wheelEventFired, description + "wheel event wasn't fired");
+
+ description = descriptionBase + "dispatching a page wheel event: ";
+ wheelEventFired = false;
+ var untrustedPageEvent = new WheelEvent("wheel", {
+ bubbles: true, cancelable: true,
+ deltaX: 1.0, deltaY: 1.0,
+ deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ });
+ gScrolledElement.dispatchEvent(untrustedPageEvent);
+ ok(wheelEventFired, description + "wheel event wasn't fired");
+
+ window.removeEventListener("wheel", wheelEventHandler, true);
+ window.removeEventListener("DOMMouseScroll", legacyEventHandler, true);
+ window.removeEventListener("MozMousePixelScroll", legacyEventHandler, true);
+}
+
+function* testEventOrder()
+{
+ const kWheelEvent = 0x0001;
+ const kDOMMouseScrollEvent = 0x0002;
+ const kMozMousePixelScrollEvent = 0x0004;
+ const kVerticalScrollEvent = 0x0010;
+ const kHorizontalScrollEvent = 0x0020;
+ const kInSystemGroup = 0x0100;
+ const kDefaultPrevented = 0x1000;
+
+ var currentTest;
+
+ const kTests = [
+ {
+ description: "Testing the order of the events without preventDefault()",
+ expectedEvents: [ kWheelEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kWheelEvent | kInSystemGroup],
+ resultEvents: [],
+ doPreventDefaultAt: 0,
+ },
+ {
+ description: "Testing the order of the events, calling preventDefault() at default group wheel event",
+ expectedEvents: [ kWheelEvent,
+ kWheelEvent | kInSystemGroup | kDefaultPrevented],
+ resultEvents: [],
+ doPreventDefaultAt: kWheelEvent,
+ },
+ {
+ description: "Testing the order of the events, calling preventDefault() at default group DOMMouseScroll event",
+ expectedEvents: [ kWheelEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent | kInSystemGroup | kDefaultPrevented,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kDefaultPrevented,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kInSystemGroup | kDefaultPrevented,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kWheelEvent | kInSystemGroup | kDefaultPrevented],
+ resultEvents: [],
+ doPreventDefaultAt: kDOMMouseScrollEvent | kVerticalScrollEvent,
+ },
+ {
+ description: "Testing the order of the events, calling preventDefault() at default group MozMousePixelScroll event",
+ expectedEvents: [ kWheelEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kInSystemGroup | kDefaultPrevented,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kWheelEvent | kInSystemGroup | kDefaultPrevented],
+ resultEvents: [],
+ doPreventDefaultAt: kMozMousePixelScrollEvent | kVerticalScrollEvent,
+ },
+ {
+ description: "Testing the order of the events, calling preventDefault() at system group DOMMouseScroll event",
+ expectedEvents: [ kWheelEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kDefaultPrevented,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kInSystemGroup | kDefaultPrevented,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kWheelEvent | kInSystemGroup | kDefaultPrevented],
+ resultEvents: [],
+ doPreventDefaultAt: kDOMMouseScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ },
+ {
+ description: "Testing the order of the events, calling preventDefault() at system group MozMousePixelScroll event",
+ expectedEvents: [ kWheelEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent,
+ kDOMMouseScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent,
+ kMozMousePixelScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent,
+ kDOMMouseScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent,
+ kMozMousePixelScrollEvent | kHorizontalScrollEvent | kInSystemGroup,
+ kWheelEvent | kInSystemGroup | kDefaultPrevented],
+ resultEvents: [],
+ doPreventDefaultAt: kMozMousePixelScrollEvent | kVerticalScrollEvent | kInSystemGroup,
+ },
+ ];
+
+ function getEventDescription(aEvent)
+ {
+ var result = "";
+ if (aEvent & kWheelEvent) {
+ result = "wheel"
+ } else {
+ if (aEvent & kDOMMouseScrollEvent) {
+ result = "DOMMouseScroll";
+ } else if (aEvent & kMozMousePixelScrollEvent) {
+ result = "MozMousePixelScroll";
+ }
+ if (aEvent & kVerticalScrollEvent) {
+ result += ", vertical";
+ } else {
+ result += ", horizontal";
+ }
+ }
+ if (aEvent & kInSystemGroup) {
+ result += ", system group";
+ }
+ if (aEvent & kDefaultPrevented) {
+ result += ", defaultPrevented";
+ }
+ return result;
+ }
+
+ function pushEvent(aEvent, aIsSystemGroup)
+ {
+ var event = 0;
+ if (aEvent.type == "wheel") {
+ event = kWheelEvent;
+ } else {
+ if (aEvent.type == "DOMMouseScroll") {
+ event = kDOMMouseScrollEvent;
+ } else if (aEvent.type == "MozMousePixelScroll") {
+ event = kMozMousePixelScrollEvent;
+ }
+ if (aEvent.axis == MouseScrollEvent.HORIZONTAL_AXIS) {
+ event |= kHorizontalScrollEvent;
+ } else {
+ event |= kVerticalScrollEvent;
+ }
+ }
+ if (aIsSystemGroup) {
+ event |= kInSystemGroup;
+ }
+ if (aEvent.defaultPrevented) {
+ event |= kDefaultPrevented;
+ }
+ currentTest.resultEvents.push(event);
+
+ if (event == currentTest.doPreventDefaultAt) {
+ aEvent.preventDefault();
+ }
+
+ if (currentTest.resultEvents.length == currentTest.expectedEvents.length) {
+ setTimeout(continueTest, 0);
+ }
+ }
+
+ function handler(aEvent)
+ {
+ pushEvent(aEvent, false);
+ }
+
+ function systemHandler(aEvent)
+ {
+ pushEvent(aEvent, true);
+ }
+
+ window.addEventListener("wheel", handler, { capture: true, passive: false });
+ window.addEventListener("DOMMouseScroll", handler, { capture: true, passive: false });
+ window.addEventListener("MozMousePixelScroll", handler, { capture: true, passive: false });
+
+ SpecialPowers.wrap(window).addEventListener("wheel", systemHandler, { capture: true, passive: false, mozSystemGroup: true });
+ SpecialPowers.wrap(window).addEventListener("DOMMouseScroll", systemHandler, { capture: true, passive: false, mozSystemGroup: true });
+ SpecialPowers.wrap(window).addEventListener("MozMousePixelScroll", systemHandler, { capture: true, passive: false, mozSystemGroup: true });
+
+ for (var i = 0; i < kTests.length; i++) {
+ currentTest = kTests[i];
+ yield synthesizeWheel(gScrollableElement, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE, deltaX: 1.0, deltaY: 1.0 });
+
+ for (var j = 0; j < currentTest.expectedEvents.length; j++) {
+ if (currentTest.resultEvents.length == j) {
+ ok(false, currentTest.description + ": " +
+ getEventDescription(currentTest.expectedEvents[j]) + " wasn't fired");
+ break;
+ }
+ is(getEventDescription(currentTest.resultEvents[j]),
+ getEventDescription(currentTest.expectedEvents[j]),
+ currentTest.description + ": " + (j + 1) + "th event is mismatched");
+ }
+ if (currentTest.expectedEvents.length < currentTest.resultEvents.length) {
+ ok(false, currentTest.description + ": " +
+ getEventDescription(currentTest.resultEvents[currentTest.expectedEvents.length]) +
+ " was fired unexpectedly");
+ }
+ }
+
+ window.removeEventListener("wheel", handler, true);
+ window.removeEventListener("DOMMouseScroll", handler, true);
+ window.removeEventListener("MozMousePixelScroll", handler, true);
+
+ window.removeEventListener("wheel", systemHandler, true);
+ window.removeEventListener("DOMMouseScroll", systemHandler, true);
+ window.removeEventListener("MozMousePixelScroll", systemHandler, true);
+}
+
+var gOnWheelAttrHandled = new Array;
+var gOnWheelAttrCount = 0;
+
+function* testOnWheelAttr()
+{
+ function onWheelHandledString(attr) {
+ return `gOnWheelAttrHandled['${attr}'] = true;
+ ++gOnWheelAttrCount;
+ if (gOnWheelAttrCount == 3) {
+ setTimeout(continueTest, 0);
+ };`;
+ }
+
+ document.documentElement.setAttribute("onwheel", onWheelHandledString("html"));
+ document.body.setAttribute("onwheel", onWheelHandledString("body"));
+ gScrollableElement.setAttribute("onwheel", onWheelHandledString("div"));
+ var target = document.getElementById("onwheel");
+ yield synthesizeWheel(gScrollableElement, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 2.0 });
+ ok(gOnWheelAttrHandled.html, "html element's onwheel attribute isn't performed");
+ ok(gOnWheelAttrHandled.body, "body element's onwheel attribute isn't performed");
+ ok(gOnWheelAttrHandled.div, "div element's onwheel attribute isn't performed");
+}
+
+var gOnWheelPropHandled = new Array;
+var gOnWheelPropCount = 0;
+
+function* testOnWheelProperty()
+{
+ const handleOnWheelProp = prop => e => {
+ gOnWheelPropHandled[prop] = true;
+ ++gOnWheelPropCount;
+ if (gOnWheelPropCount == 5) {
+ setTimeout(continueTest, 0);
+ }
+ }
+
+ window.onwheel = handleOnWheelProp('window');
+ document.onwheel = handleOnWheelProp('document');
+ document.documentElement.onwheel = handleOnWheelProp('html');
+ document.body.onwheel = handleOnWheelProp('body');
+ gScrollableElement.onwheel = handleOnWheelProp('div');
+
+ var target = document.getElementById("onwheel");
+ yield synthesizeWheel(gScrollableElement, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 2.0 });
+
+ ok(gOnWheelPropHandled.window, "window's onwheel property isn't performed");
+ ok(gOnWheelPropHandled.document, "document's onwheel property isn't performed");
+ ok(gOnWheelPropHandled.html, "html element's onwheel property isn't performed");
+ ok(gOnWheelPropHandled.body, "body element's onwheel property isn't performed");
+ ok(gOnWheelPropHandled.div, "div element's onwheel property isn't performed");
+}
+
+function* testBody()
+{
+ yield* prepareScrollUnits();
+ testMakingUntrustedEvent();
+ yield* testDeltaMultiplierPrefs();
+ testDispatchingUntrustEvent();
+ yield* testEventOrder();
+ yield* testOnWheelAttr();
+ yield* testOnWheelProperty();
+}
+
+var gTestContinuation = null;
+
+function continueTest()
+{
+ if (!gTestContinuation) {
+ gTestContinuation = testBody();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ }
+}
+
+function runTest()
+{
+ SpecialPowers.pushPrefEnv({"set": [
+ // FIXME(emilio): This test is broken in HiDPI, unclear if
+ // MozMousePixelScroll is not properly converting to CSS pixels, or
+ // whether sendWheelAndWait expectes device rather than CSS pixels, or
+ // something else.
+ ["layout.css.devPixelsPerPx", 1.0],
+
+ ["dom.event.wheel-deltaMode-lines.disabled", true],
+
+ ["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100],
+ ["mousewheel.default.delta_multiplier_z", 100],
+ ["mousewheel.with_alt.delta_multiplier_x", 100],
+ ["mousewheel.with_alt.delta_multiplier_y", 100],
+ ["mousewheel.with_alt.delta_multiplier_z", 100],
+ ["mousewheel.with_control.delta_multiplier_x", 100],
+ ["mousewheel.with_control.delta_multiplier_y", 100],
+ ["mousewheel.with_control.delta_multiplier_z", 100],
+ ["mousewheel.with_meta.delta_multiplier_x", 100],
+ ["mousewheel.with_meta.delta_multiplier_y", 100],
+ ["mousewheel.with_meta.delta_multiplier_z", 100],
+ ["mousewheel.with_shift.delta_multiplier_x", 100],
+ ["mousewheel.with_shift.delta_multiplier_y", 100],
+ ["mousewheel.with_shift.delta_multiplier_z", 100],
+ ]}, continueTest);
+}
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_drag_coords.html b/dom/events/test/test_drag_coords.html
new file mode 100644
index 0000000000..2db65a66f9
--- /dev/null
+++ b/dom/events/test/test_drag_coords.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<title>Test for drag event coordinates</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<style>
+ #draggable {
+ display: inline-block;
+ border: 1px solid;
+ }
+</style>
+<div draggable="true" id="draggable">Drag me</div>
+<script>
+add_task(async function test_drag_coords() {
+ await SpecialPowers.contentTransformsReceived(window);
+
+ let target = document.getElementById("draggable");
+ let coords = {};
+ let promises = [];
+ for (let type of ["dragstart", "dragend"]) {
+ promises.push(new Promise(function(resolve) {
+ target.addEventListener(type, function(e) {
+ info("Got " + e.type);
+ coords[e.type] = {
+ screen: {
+ x: e.screenX,
+ y: e.screenY,
+ },
+ client: {
+ x: e.clientX,
+ y: e.clientY,
+ },
+ };
+ resolve();
+ });
+ }));
+ }
+ synthesizePlainDragAndDrop({
+ srcElement: target,
+ srcX: 2,
+ srcY: 2,
+ stepX: 10,
+ stepY: 10,
+ });
+ await Promise.all(promises);
+ info(JSON.stringify(coords));
+ for (let coordType of ["screen", "client"]) {
+ is(coords.dragend[coordType].x, coords.dragstart[coordType].x + 12, `x ${coordType} is correct`);
+ is(coords.dragend[coordType].y, coords.dragstart[coordType].y + 12, `y ${coordType} is correct`);
+ }
+});
+</script>
diff --git a/dom/events/test/test_drag_image_file.html b/dom/events/test/test_drag_image_file.html
new file mode 100644
index 0000000000..13e6996693
--- /dev/null
+++ b/dom/events/test/test_drag_image_file.html
@@ -0,0 +1,56 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test that dragging an image produces a File</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>
+
+<img id="green-png" src="green.png">
+
+<script>
+async function runTest() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.events.dataTransfer.imageAsFile.enabled", true]],
+ });
+
+ let dt = await synthesizePlainDragAndCancel({
+ srcElement: document.getElementById('green-png'),
+ finalY: 20,
+ }, null);
+
+ info(`DataTransfer types: ${dt.types}`);
+
+ for (let type of dt.types) {
+ info(`getData(${type}) = ${dt.getData(type)}`)
+ }
+
+ ok(dt.types.includes("Files"), "types should contains 'Files'");
+ is(dt.files.length, 1, "files contains one File");
+
+ let fileItem = null;
+ for (let item of dt.items) {
+ if (item.kind === "file") {
+ fileItem = item;
+ break;
+ }
+ }
+
+ is(fileItem.kind, "file", "Is a file");
+ is(fileItem.type, "image/png", "Is a PNG file");
+
+ let file = fileItem.getAsFile();
+ is(file.name, "image.png", "Has generic image name")
+ ok(file.size > 100, "Is not empty");
+
+ SimpleTest.finish();
+}
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTest);
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_draggableprop.html b/dom/events/test/test_draggableprop.html
new file mode 100644
index 0000000000..af2d9906c2
--- /dev/null
+++ b/dom/events/test/test_draggableprop.html
@@ -0,0 +1,93 @@
+<html>
+<head>
+ <title>Tests for the draggable property on HTML elements</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<span id="elem1">One</span>
+<span id="elem2" draggable="true">Two</span>
+<span id="elem3" draggable="">Three</span>
+<span id="elem4" draggable="false">Four</span>
+<span id="elem5" draggable="other">Five</span>
+<span id="elem6" draggable="false"><span style="user-select: none">Six</span></span>
+
+<img id="img1" src="../happy.png">
+<img id="img2" src="../happy.png" draggable="true">
+<img id="img3" src="../happy.png" draggable="">
+<img id="img4" src="../happy.png" draggable="false">
+<img id="img5" src="../happy.png" draggable="other">
+<img id="img6" src="../happy.png" draggable="false">
+
+<a id="a1">One</a>
+<a id="a2" draggable="true">Two</a>
+<a id="a3" draggable="">Three</a>
+<a id="a4" draggable="false">Four</a>
+<a id="a5" draggable="other">Five</a>
+<a id="a6" draggable="false"><span style="user-select: none">Six</span></a>
+
+<a id="ahref1" href="http://www.mozilla.org">One</a>
+<a id="ahref2" href="http://www.mozilla.org" draggable="true">Two</a>
+<a id="ahref3" href="http://www.mozilla.org" draggable="">Three</a>
+<a id="ahref4" href="http://www.mozilla.org" draggable="false">Four</a>
+<a id="ahref5" href="http://www.mozilla.org" draggable="other">Five</a>
+<a id="ahref6" href="http://www.mozilla.org" draggable="false"><span style="user-select: none">Six</span></a>
+
+<script>
+function check()
+{
+ try {
+ checkElements(1, false, true, false, true);
+ checkElements(2, true, true, true, true);
+ checkElements(3, false, true, false, true);
+ checkElements(4, false, false, false, false);
+ checkElements(5, false, true, false, true);
+ checkElements(6, false, false, false, false);
+ } catch (ex) {
+ is("script error", ex, "fail");
+ }
+}
+
+function checkElements(idx, estate, istate, astate, ahrefstate)
+{
+ checkElement("elem" + idx, estate, false);
+ checkElement("img" + idx, istate, true);
+ checkElement("a" + idx, astate, false);
+ checkElement("ahref" + idx, ahrefstate, true);
+}
+
+function checkElement(elemid, state, statedef)
+{
+ var elem = document.getElementById(elemid);
+
+ is(elem.draggable, state, elemid + "-initial");
+ elem.draggable = true;
+ is(elem.draggable, true, elemid + "-true");
+ elem.draggable = false;
+ is(elem.draggable, false, elemid + "-false");
+
+ elem.setAttribute("draggable", "true");
+ is(elem.draggable, true, elemid + "-attr-true");
+ elem.setAttribute("draggable", "false");
+ is(elem.draggable, false, elemid + "-attr-false");
+ elem.setAttribute("draggable", "other");
+ is(elem.draggable, statedef, elemid + "-attr-other");
+ elem.setAttribute("draggable", "");
+ is(elem.draggable, statedef, elemid + "-attr-empty");
+ elem.removeAttribute("draggable");
+ is(elem.draggable, statedef, elemid + "-attr-removed");
+}
+
+check();
+
+</script>
+
+</body>
+</html>
+
+
diff --git a/dom/events/test/test_dragstart.html b/dom/events/test/test_dragstart.html
new file mode 100644
index 0000000000..4c87cc3c53
--- /dev/null
+++ b/dom/events/test/test_dragstart.html
@@ -0,0 +1,716 @@
+<html>
+<head>
+ <title>Tests for the dragstart event</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+
+<!--
+ This test checks the dragstart event and the DataTransfer object
+ -->
+
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+var gDragInfo;
+var gDataTransfer = null;
+var gExtraDragTests = 0;
+
+function runTests()
+{
+ // first, create a selection and try dragging it
+ var draggable = $("draggable");
+ window.getSelection().selectAllChildren(draggable);
+ synthesizeMouse(draggable, 6, 6, { type: "mousedown" });
+ synthesizeMouse(draggable, 14, 14, { type: "mousemove" });
+ // drags are asynchronous on Linux, so this extra event is needed to make
+ // sure the drag gets processed
+ synthesizeMouse(draggable, 15, 15, { type: "mousemove" });
+}
+
+function afterDragTests()
+{
+ // the dragstart should have occurred due to moving the mouse. gDataTransfer
+ // caches the dataTransfer that was used, however it should now be empty and
+ // be read only.
+ ok(gDataTransfer instanceof DataTransfer, "DataTransfer after dragstart event");
+ checkTypes(gDataTransfer, [], 0, "after dragstart event");
+
+ expectError(() => gDataTransfer.setData("text/plain", "Some Text"),
+ "NoModificationAllowedError", "setData when read only");
+ expectError(() => gDataTransfer.clearData("text/plain"),
+ "NoModificationAllowedError", "clearData when read only");
+ expectError(() => gDataTransfer.addElement(draggable),
+ "NoModificationAllowedError", "addElement when read only");
+
+ var evt = document.createEvent("dragevent");
+ ok(evt instanceof DragEvent, "synthetic dragevent class")
+ ok(evt instanceof MouseEvent, "synthetic event inherits from MouseEvent")
+ evt.initDragEvent("dragstart", true, true, window, 1, 40, 35, 20, 15,
+ false, true, false, false, 0, null, null);
+ $("synthetic").dispatchEvent(evt);
+
+ var evt = document.createEvent("dragevent");
+ ok(evt instanceof DragEvent, "synthetic dragevent class")
+ evt.initDragEvent("dragover", true, true, window, 0, 40, 35, 20, 15,
+ true, false, true, true, 2, document.documentElement, null);
+ $("synthetic2").dispatchEvent(evt);
+
+ // next, dragging links and images
+ sendMouseEventsForDrag("link");
+ sendMouseEventsForDrag("image");
+
+// disable testing input dragging for now, as it doesn't seem to be testable
+// draggable = $("input");
+// draggable.setSelectionRange(0, 4);
+// synthesizeMouse(draggable, 8, 8, { type: "mousedown" });
+// synthesizeMouse(draggable, 15, 15, { type: "mousemove" });
+// sendMouseEventsForDrag("input");
+
+ // draggable elements inside a shadow root
+ sendMouseEventsForShadowRootDrag("shadow_host_containing_draggable");
+ sendMouseEventsForShadowRootDrag("shadow_host_containing_image");
+
+ // next, check if the draggable attribute can be used to adjust the drag target
+ gDragInfo = { target: $("dragtrue"), testid: "draggable true node" };
+ sendMouseEventsForDrag("dragtrue");
+ gDragInfo = { target: $("dragtrue"), testid: "draggable true child" };
+ sendMouseEventsForDrag("spantrue");
+
+ gDragInfo = { target: $("dragfalse").firstChild, testid: "draggable false node" };
+ sendMouseEventsForDrag("dragfalse");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ gDragInfo = { target: $("spanfalse").firstChild, testid: "draggable false child" };
+ sendMouseEventsForDrag("spanfalse");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ gDragInfo = { target: $("userselectnone").firstChild, testid: "user select none inside draggable false node" };
+ sendMouseEventsForDrag("userselectnone");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ gDragInfo = { target: $("draggable_with_undraggable_descendant"), testid: "undraggable inside draggable" };
+ sendMouseEventsForDrag("undraggable_inside_draggable");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ sendMouseEventsForDrag("link_to_file");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ sendMouseEventsForDrag("broken_file_image");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ if (navigator.platform.startsWith("Win")) {
+ sendMouseEventsForDrag("link_to_windows_file");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ sendMouseEventsForDrag("broken_windows_file_image");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ sendMouseEventsForDrag("link_to_backslash_windows_file");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+
+ sendMouseEventsForDrag("broken_backslash_windows_file_image");
+ synthesizeMouse(draggable, 12, 12, { type: "mouseup" });
+ }
+
+ if (gExtraDragTests == 7)
+ SimpleTest.finish();
+}
+
+function sendMouseEventsForDrag(nodeid)
+{
+ info("Sending events for " + nodeid);
+ var draggable = $(nodeid);
+ synthesizeMouse(draggable, 3, 3, { type: "mousedown" });
+ synthesizeMouse(draggable, 10, 10, { type: "mousemove" });
+ synthesizeMouse(draggable, 12, 12, { type: "mousemove" });
+}
+function sendMouseEventsForShadowRootDrag(host)
+{
+ var draggable = $(host).shadowRoot.firstElementChild;
+ synthesizeMouse(draggable, 3, 3, { type: "mousedown" });
+ synthesizeMouse(draggable, 10, 10, { type: "mousemove" });
+ synthesizeMouse(draggable, 12, 12, { type: "mousemove" });
+}
+
+function doDragStartSelection(event)
+{
+ is(event.type, "dragstart", "dragstart event type");
+ is(event.target, $("draggable").firstChild, "dragstart event target");
+ is(event.bubbles, true, "dragstart event bubbles");
+ is(event.cancelable, true, "dragstart event cancelable");
+
+ is(event.clientX, 14, "dragstart clientX");
+ is(event.clientY, 14, "dragstart clientY");
+ ok(event.screenX > 0, "dragstart screenX");
+ ok(event.screenY > 0, "dragstart screenY");
+ is(event.layerX, 14, "dragstart layerX");
+ is(event.layerY, 14, "dragstart layerY");
+ is(event.pageX, 14, "dragstart pageX");
+ is(event.pageY, 14, "dragstart pageY");
+
+ var dt = event.dataTransfer;
+ ok(dt instanceof DataTransfer, "dataTransfer is DataTransfer");
+ gDataTransfer = dt;
+
+ var types = dt.types;
+ ok(Array.isArray(types), "initial types is an Array");
+ checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection");
+
+ is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
+ is(dt.getData("text/html"), "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>",
+ "initial selection text/html");
+
+ // text/plain and Text are available for compatibility. They retrieve the
+ // text/plain data. text/unicode is also for compatibility and retreives the plain text.
+ is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
+ is(dt.getData("Text"), "This is a draggable bit of text.", "initial selection Text");
+ is(dt.getData("TEXT"), "This is a draggable bit of text.", "initial selection TEXT");
+ is(dt.getData("text/PLAIN"), "This is a draggable bit of text.", "initial selection text/PLAIN");
+ is(dt.getData("text/unicode"), "This is a draggable bit of text.", "initial selection text/unicode");
+
+ is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial selection item count");
+
+ dt.clearData("text/plain");
+ dt.clearData("text/html");
+ dt.clearData("text/_moz_htmlinfo");
+ dt.clearData("text/_moz_htmlcontext");
+
+ test_DataTransfer(dt);
+ setTimeout(afterDragTests, 0);
+}
+
+function test_DataTransfer(dt)
+{
+ is(SpecialPowers.wrap(dt).mozItemCount, 0, "empty itemCount");
+
+ var types = dt.types;
+ ok(Array.isArray(types), "empty types is an Array");
+ // The above test fails if we have SpecialPowers.wrap(dt).types instead of dt.types
+ // because dt.types will be wrapped and become a proxy.
+ // So wrap with special powers after the test
+ dt = SpecialPowers.wrap(dt);
+ checkTypes(dt, [], 0, "empty");
+ is(dt.getData("text/plain"), "", "empty data is empty");
+
+ // calling setDataAt requires an index that is 0 <= index <= dt.itemCount
+ expectError(() => dt.mozSetDataAt("text/plain", "Some Text", 1),
+ "IndexSizeError", "setDataAt index too high");
+
+ is(dt.mozUserCancelled, false, "userCancelled");
+
+ // because an exception occurred, the data should not have been added
+ is(dt.mozItemCount, 0, "empty setDataAt index too high itemCount");
+ dt.getData("text/plain", "", "empty setDataAt index too high getData");
+
+ // if the type is '', do nothing, or return ''
+ dt.setData("", "Invalid Type");
+ is(dt.types.length, 0, "invalid type setData");
+ is(dt.getData(""), "", "invalid type getData");
+ dt.mozSetDataAt("", "Invalid Type", 0);
+ is(dt.types.length, 0, "invalid type setDataAt");
+ is(dt.mozGetDataAt("", 0), null, "invalid type getDataAt");
+
+ // similar with clearDataAt and getDataAt
+ expectError(() => dt.mozGetDataAt("text/plain", 1),
+ "IndexSizeError", "getDataAt index too high");
+ expectError(() => dt.mozClearDataAt("text/plain", 1),
+ "IndexSizeError", "clearDataAt index too high");
+
+ dt.setData("text/plain", "Sample Text");
+ is(dt.mozItemCount, 1, "added plaintext itemCount");
+ checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext");
+
+ // after all those exceptions, the data should still be the same
+ checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext after exception");
+
+ // modifying the data associated with the format should give it the new value
+ dt.setData("text/plain", "Modified Text");
+ is(dt.mozItemCount, 1, "modified plaintext itemCount");
+ checkOneDataItem(dt, ["text/plain"], ["Modified Text"], 0, "modified plaintext");
+
+ dt.setData("text/html", "<strong>Modified Text</strong>");
+ is(dt.mozItemCount, 1, "modified html itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Modified Text", "<strong>Modified Text</strong>"],
+ 0, "modified html");
+
+ // modifying data for a type that already exists should adjust it in place,
+ // not reinsert it at the beginning
+ dt.setData("text/plain", "New Text");
+ is(dt.mozItemCount, 1, "modified text again itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["New Text", "<strong>Modified Text</strong>"],
+ 0, "modified text again");
+
+ var draggable = $("draggable");
+ dt.setData("application/-moz-node", draggable);
+ checkOneDataItem(dt, ["text/plain", "text/html", "application/-moz-node"],
+ ["New Text", "<strong>Modified Text</strong>", draggable.toString()],
+ 0, "added node");
+
+ dt.clearData(""); // null means clear all
+ is(dt.mozItemCount, 0, "itemCount after clearData empty string");
+ checkTypes(dt, [], 0, "empty after clearData empty string");
+
+ dt.setData("text/plain", 22);
+ dt.setData("text/html", 5.6);
+ dt.setData("text/xml", 5.6);
+ checkTypes(dt, ["text/plain", "text/html", "text/xml"], ["22", "5.6", ""], 0, "add numeric and empty data");
+
+ dt.clearData(); // no argument means clear all
+ is(dt.mozItemCount, 0, "itemCount after clearData no argument");
+ checkTypes(dt, [], 0, "empty after clearData no argument");
+
+ // check 'Text' type which should convert into text/plain
+ dt.setData("Text", "Sample Text");
+ checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "set Text");
+ is(dt.getData("Text"), "Sample Text", "getData Text");
+ is(dt.mozGetDataAt("Text", 0), "Sample Text", "getDataAt Text");
+ dt.setData("text/plain", "More Text");
+ checkOneDataItem(dt, ["text/plain"], ["More Text"], 0, "set text/plain after set Text");
+
+ dt.mozClearDataAt("", 0); // null means clear all
+ is(dt.mozItemCount, 0, "itemCount after clearDataAt empty string");
+ checkTypes(dt, [], 0, "empty after clearDataAt empty string");
+
+ // check text/uri-list type
+ dt.setData("text/uri-list", "http://www.mozilla.org");
+ checkURL(dt, "http://www.mozilla.org", "http://www.mozilla.org", 0, "set text/uri-list");
+
+ // check URL type which should add text/uri-list data
+ dt.setData("URL", "ftp://ftp.example.com");
+ checkURL(dt, "ftp://ftp.example.com", "ftp://ftp.example.com", 0, "set URL");
+ checkTypes(dt, ["text/uri-list"], ["ftp://ftp.example.com"], "url types");
+
+ // clearing text/uri-list data
+ dt.clearData("text/uri-list");
+ is(dt.mozItemCount, 0, "itemCount after clear url-list");
+ is(dt.getData("text/uri-list"), "", "text/uri-list after clear url-list");
+ is(dt.getData("URL"), "", "URL after clear url-list");
+
+ // check text/uri-list parsing
+ dt.setData("text/uri-list", "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com");
+ checkURL(dt, "http://www.xulplanet.com",
+ "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com",
+ 0, "uri-list 3 lines");
+
+ dt.setData("text/uri-list", "#http://www.mozilla.org");
+ is(dt.getData("URL"), "", "uri-list commented");
+ dt.setData("text/uri-list", "#http://www.mozilla.org\n");
+ is(dt.getData("URL"), "", "uri-list commented with newline");
+
+ // check that clearing the URL type also clears the text/uri-list type
+ dt.clearData("URL");
+ is(dt.getData("text/uri-list"), "", "clear URL");
+
+ dt.setData("text/uri-list", "#http://www.mozilla.org\n\n\n\n\n");
+ is(dt.getData("URL"), "", "uri-list with blank lines");
+ dt.setData("text/uri-list", "");
+ is(dt.getData("URL"), "", "empty uri-list");
+ dt.setData("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com \r\n");
+ is(dt.getData("URL"), "http://www.xulplanet.com", "uri-list mix");
+ dt.setData("text/uri-list", "\nhttp://www.mozilla.org");
+ is(dt.getData("URL"), "", "empty line to start uri-list");
+ dt.setData("text/uri-list", " http://www.mozilla.org#anchor ");
+ is(dt.getData("URL"), "http://www.mozilla.org#anchor", "uri-list with spaces and hash");
+
+ // ensure that setDataAt works the same way
+ dt.mozSetDataAt("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com \r\n", 0);
+ checkURL(dt, "http://www.xulplanet.com",
+ "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com \r\n",
+ 0, "uri-list mix setDataAt");
+
+ // now test adding multiple items to be dragged using the setDataAt method
+ dt.clearData();
+ dt.mozSetDataAt("text/plain", "First Item", 0);
+ dt.mozSetDataAt("text/plain", "Second Item", 1);
+ expectError(() => dt.mozSetDataAt("text/plain", "Some Text", 3),
+ "IndexSizeError", "setDataAt index too high with two items");
+ is(dt.mozItemCount, 2, "setDataAt item itemCount");
+ checkOneDataItem(dt, ["text/plain"], ["First Item"], 0, "setDataAt item at index 0");
+ checkOneDataItem(dt, ["text/plain"], ["Second Item"], 1, "setDataAt item at index 1");
+
+ dt.mozSetDataAt("text/html", "<em>First Item</em>", 0);
+ dt.mozSetDataAt("text/html", "<em>Second Item</em>", 1);
+ is(dt.mozItemCount, 2, "setDataAt two types item itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "<em>First Item</em>"], 0, "setDataAt two types item at index 0");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Second Item", "<em>Second Item</em>"], 1, "setDataAt two types item at index 1");
+
+ dt.mozSetDataAt("text/html", "<em>Changed First Item</em>", 0);
+ dt.mozSetDataAt("text/plain", "Changed Second Item", 1);
+ is(dt.mozItemCount, 2, "changed with setDataAt item itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "<em>Changed First Item</em>"], 0, "changed with setDataAt item at index 0");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setDataAt item at index 1");
+
+ dt.setData("text/html", "Changed with setData");
+ is(dt.mozItemCount, 2, "changed with setData");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "changed with setData item at index 0");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setData item at index 1");
+
+ dt.mozSetDataAt("application/-moz-node", "draggable", 2);
+ is(dt.mozItemCount, 3, "setDataAt node itemCount");
+ checkOneDataItem(dt, ["application/-moz-node"], ["draggable"], 2, "setDataAt node item at index 2");
+
+ // Try to add and then remove a non-string type to the DataTransfer and ensure
+ // that the type appears in DataTransfer.types.
+ {
+ dt.mozSetDataAt("application/-x-body", document.body, 0);
+ let found = false;
+ for (let i = 0; i < dt.types.length; ++i) {
+ if (dt.types[i] == "application/-x-body") {
+ found = true;
+ break;
+ }
+ }
+ ok(found, "Data should appear in datatransfer.types despite having a non-string type");
+ dt.mozClearDataAt("application/-x-body", 0);
+ }
+
+ dt.mozClearDataAt("text/html", 1);
+ is(dt.mozItemCount, 3, "clearDataAt itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearDataAt item at index 0");
+ checkOneDataItem(dt, ["text/plain"], ["Changed Second Item"], 1, "clearDataAt item at index 1");
+
+ dt.mozClearDataAt("text/plain", 1);
+ is(dt.mozItemCount, 2, "clearDataAt last type itemCount");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearDataAt last type at index 0");
+ checkOneDataItem(dt, ["application/-moz-node"], ["draggable"], 1, "clearDataAt last type item at index 2");
+ expectError(() => dt.mozGetDataAt("text/plain", 2),
+ "IndexSizeError", "getDataAt after item removed index too high");
+
+ dt.mozSetDataAt("text/unknown", "Unknown type", 2);
+ dt.mozSetDataAt("text/unknown", "Unknown type", 1);
+ is(dt.mozItemCount, 3, "add unknown type");
+ checkOneDataItem(dt, ["application/-moz-node", "text/unknown"],
+ ["draggable", "Unknown type"], 1, "add unknown type item at index 1");
+ checkOneDataItem(dt, ["text/unknown"], ["Unknown type"], 2, "add unknown type item at index 2");
+
+ dt.mozClearDataAt("", 1);
+ is(dt.mozItemCount, 2, "clearDataAt empty string");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearDataAt empty string item at index 0");
+ checkOneDataItem(dt, ["text/unknown"],
+ ["Unknown type"], 1, "clearDataAt empty string item at index 1");
+
+ // passing a format that doesn't exist to clearData or clearDataAt should just
+ // do nothing
+ dt.clearData("text/something");
+ dt.mozClearDataAt("text/something", 1);
+ is(dt.mozItemCount, 2, "clearData type that does not exist");
+ checkOneDataItem(dt, ["text/plain", "text/html"],
+ ["First Item", "Changed with setData"], 0, "clearData type that does not exist item at index 0");
+ checkOneDataItem(dt, ["text/unknown"],
+ ["Unknown type"], 1, "clearData type that does not exist item at index 1");
+
+ expectError(() => dt.mozClearDataAt("text/plain", 3),
+ "IndexSizeError", "clearData index too high with two items");
+
+ // ensure that clearData() removes all data associated with the first item, but doesn't
+ // shift the second item down into the first item's slot.
+ dt.clearData();
+ is(dt.mozItemCount, 2, "clearData no argument with multiple items itemCount");
+ checkOneDataItem(dt, [], [], 0,
+ "clearData no argument with multiple items item at index 0");
+ checkOneDataItem(dt, ["text/unknown"],
+ ["Unknown type"], 1, "clearData no argument with multiple items item at index 1");
+
+ // remove tha remaining data in index 1. As index 0 is empty at this point, this will actually
+ // drop mozItemCount to 0. (XXX: This is because of spec-compliance reasons related
+ // to the more-recent dt.item API. It's an unfortunate, but hopefully rare edge case)
+ dt.mozClearDataAt("", 1);
+ is(dt.mozItemCount, 0, "all data cleared");
+
+ // now check the effectAllowed and dropEffect properties
+ is(dt.dropEffect, "none", "initial dropEffect");
+ is(dt.effectAllowed, "uninitialized", "initial effectAllowed");
+
+ ["copy", "none", "link", "", "other", "copyMove", "all", "uninitialized", "move"].forEach(
+ function (i) {
+ dt.dropEffect = i;
+ is(dt.dropEffect, i == "" || i == "other" || i == "copyMove" ||
+ i == "all" || i == "uninitialized" ? "link" : i,
+ "dropEffect set to " + i);
+ is(dt.effectAllowed, "uninitialized", "effectAllowed not modified by dropEffect set to " + i);
+ }
+ );
+
+ ["move", "copy", "link", "", "other", "moveCopy", "copyMove",
+ "linkMove", "copyLink", "all", "uninitialized", "none"].forEach(
+ function (i) {
+ dt.effectAllowed = i;
+ is(dt.dropEffect, "move", "dropEffect not modified by effectAllowed set to " + i);
+ is(dt.effectAllowed, i == "" || i == "other" || i == "moveCopy" ? "link" : i,
+ "effectAllowed set to " + i);
+ }
+ );
+}
+
+function doDragStartLink(event)
+{
+ var dt = event.dataTransfer;
+ checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+ "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial link");
+
+ is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial link item count");
+ is(dt.getData("text/uri-list"), "http://www.mozilla.org/", "link text/uri-list");
+ is(dt.getData("text/plain"), "http://www.mozilla.org/", "link text/plain");
+
+ event.preventDefault();
+
+ gExtraDragTests++;
+}
+
+function doDragStartImage(event)
+{
+ var dataurl = $("image").src;
+
+ var dt = event.dataTransfer;
+ checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+ "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial image");
+
+ is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial image item count");
+ is(dt.getData("text/uri-list"), dataurl, "image text/uri-list");
+ is(dt.getData("text/plain"), dataurl, "image text/plain");
+
+ event.preventDefault();
+
+ gExtraDragTests++;
+}
+
+function doDragStartInput(event)
+{
+ var dt = event.dataTransfer;
+ checkTypes(dt, ["text/plain"], 0, "initial input");
+
+ is(SpecialPowers.wrap(dt).mozItemCount, 1, "initial input item count");
+// is(dt.getData("text/plain"), "Text", "input text/plain");
+
+// event.preventDefault();
+}
+
+
+function doDragStartInShadowRoot(event)
+{
+ is(event.type, "dragstart", "shadow root dragstart event type");
+ is(event.target, $("shadow_host_containing_draggable"), "shadow root dragstart event target");
+ is(event.bubbles, true, "shadow root dragstart event bubbles");
+ is(event.cancelable, true, "shadow root dragstart event cancelable");
+
+ event.preventDefault();
+
+ gExtraDragTests++;
+}
+
+function doDragStartShadowRootImage(event)
+{
+ var dt = event.dataTransfer;
+
+ checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+ "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "shadow root link with image");
+
+ is(dt.getData("text/uri-list"), "http://example.org/", "shadow root link text/uri-list");
+ is(dt.getData("text/plain"), "http://example.org/", "shadow root link text/plain");
+
+ event.preventDefault();
+
+ gExtraDragTests++;
+}
+
+function doDragStartSynthetic(event)
+{
+ is(event.type, "dragstart", "synthetic dragstart event type");
+
+ var dt = event.dataTransfer;
+ todo(dt instanceof DataTransfer, "synthetic dragstart dataTransfer is DataTransfer");
+// Uncomment next line once the todo instanceof above is fixed.
+// checkTypes(dt, [], 0, "synthetic dragstart");
+
+ is(event.detail, 1, "synthetic dragstart detail");
+ is(event.screenX, 40, "synthetic dragstart screenX");
+ is(event.screenY, 35, "synthetic dragstart screenY");
+ is(event.clientX, 20, "synthetic dragstart clientX");
+ is(event.clientY, 15, "synthetic dragstart clientY");
+ is(event.ctrlKey, false, "synthetic dragstart ctrlKey");
+ is(event.altKey, true, "synthetic dragstart altKey");
+ is(event.shiftKey, false, "synthetic dragstart shiftKey");
+ is(event.metaKey, false, "synthetic dragstart metaKey");
+ is(event.button, 0, "synthetic dragstart button ");
+ is(event.relatedTarget, null, "synthetic dragstart relatedTarget");
+
+// Uncomment next two lines once the todo instanceof above is fixed.
+// dt.setData("text/plain", "Text");
+// is(dt.getData("text/plain"), "Text", "synthetic dragstart data is set after adding");
+}
+
+function doDragOverSynthetic(event)
+{
+ is(event.type, "dragover", "synthetic dragover event type");
+
+ var dt = event.dataTransfer;
+ todo(dt instanceof DataTransfer, "synthetic dragover dataTransfer is DataTransfer");
+// Uncomment next line once the todo instanceof above is fixed.
+// checkTypes(dt, [], 0, "synthetic dragover");
+
+ is(event.detail, 0, "synthetic dragover detail");
+ is(event.screenX, 40, "synthetic dragover screenX");
+ is(event.screenY, 35, "synthetic dragover screenY");
+ is(event.clientX, 20, "synthetic dragover clientX");
+ is(event.clientY, 15, "synthetic dragover clientY");
+ is(event.ctrlKey, true, "synthetic dragover ctrlKey");
+ is(event.altKey, false, "synthetic dragover altKey");
+ is(event.shiftKey, true, "synthetic dragover shiftKey");
+ is(event.metaKey, true, "synthetic dragover metaKey");
+ is(event.button, 2, "synthetic dragover button");
+ is(event.relatedTarget, document.documentElement, "synthetic dragover relatedTarget");
+
+// Uncomment next two lines once the todo instanceof above is fixed.
+// dt.setData("text/plain", "Text");
+// is(dt.getData("text/plain"), "Text", "synthetic dragover data is set after adding");
+}
+
+function onDragStartDraggable(event)
+{
+ var dt = event.dataTransfer;
+ ok(SpecialPowers.wrap(dt).mozItemCount == 0 && !dt.types.length && event.originalTarget == gDragInfo.target, gDragInfo.testid);
+
+ event.preventDefault();
+ gExtraDragTests++;
+}
+
+// Expects dt wrapped in SpecialPowers
+function checkOneDataItem(dt, expectedtypes, expecteddata, index, testid)
+{
+ checkTypes(dt, expectedtypes, index, testid);
+ for (var f = 0; f < expectedtypes.length; f++) {
+ if (index == 0)
+ is(dt.getData(expectedtypes[f]), expecteddata[f], testid + " getData " + expectedtypes[f]);
+ is(dt.mozGetDataAt(expectedtypes[f], index), expecteddata[f] ? expecteddata[f] : null,
+ testid + " getDataAt " + expectedtypes[f]);
+ }
+}
+
+function checkTypes(dt, expectedtypes, index, testid)
+{
+ if (index == 0) {
+ var types = dt.types;
+ is(types.length, expectedtypes.length, testid + " types length");
+ for (var f = 0; f < expectedtypes.length; f++) {
+ is(types[f], expectedtypes[f], testid + " " + types[f] + " check");
+ }
+ }
+
+ types = SpecialPowers.wrap(dt).mozTypesAt(index);
+ is(types.length, expectedtypes.length, testid + " typesAt length");
+ for (var f = 0; f < expectedtypes.length; f++) {
+ is(types[f], expectedtypes[f], testid + " " + types[f] + " at " + index + " check");
+ }
+}
+
+// Expects dt wrapped in SpecialPowers
+function checkURL(dt, url, fullurllist, index, testid)
+{
+ is(dt.getData("text/uri-list"), fullurllist, testid + " text/uri-list");
+ is(dt.getData("URL"), url, testid + " URL");
+ is(dt.mozGetDataAt("text/uri-list", 0), fullurllist, testid + " text/uri-list");
+ is(dt.mozGetDataAt("URL", 0), fullurllist, testid + " URL");
+}
+
+function onDragOverDraggableFalse(event) {
+ ok(false, "Triggered dragstart on draggable=false node " + event.target.id);
+}
+
+function onDragStartUnlinkable(event) {
+ ok(false, "Triggered dragstart on undraggable node " + event.target.id);
+}
+
+function expectError(fn, eid, testid)
+{
+ var error = "";
+ try {
+ fn();
+ } catch (ex) {
+ error = ex.name;
+ }
+ is(error, eid, testid + " causes exception " + eid);
+}
+
+</script>
+
+</head>
+
+<body style="height: 300px; overflow: auto;" onload="setTimeout(runTests, 0)">
+
+<div id="draggable" ondragstart="doDragStartSelection(event)">This is a <em>draggable</em> bit of text.</div>
+
+<fieldset>
+<a id="link" href="http://www.mozilla.org/" ondragstart="doDragStartLink(event)">mozilla.org</a>
+</fieldset>
+
+<label>
+<img id="image" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"
+ ondragstart="doDragStartImage(event)">
+</label>
+
+<input id="input" value="Text in a box" ondragstart="doDragStartInput(event)">
+
+<div ondragstart="onDragStartDraggable(event)">
+ <div id="dragtrue" draggable="true">
+ This is a <span id="spantrue">draggable</span> area.
+ </div>
+ <div id="dragfalse" draggable="false">
+ This is a <span id="spanfalse">non-draggable</span> area.
+ </div>
+</div>
+
+<!--iframe src="http://www.mozilla.org" width="400" height="400"></iframe-->
+
+<div id="synthetic" ondragstart="doDragStartSynthetic(event)">Synthetic Event Dispatch</div>
+<div id="synthetic2" ondragover="doDragOverSynthetic(event)">Synthetic Event Dispatch</div>
+
+<div draggable="true" id="shadow_host_containing_draggable"></div>
+
+<script>
+shadow_host_containing_draggable.attachShadow({ mode: 'open' }).innerHTML =
+`<span>Inside shadow root</span>`;
+shadow_host_containing_draggable.addEventListener("dragstart", doDragStartInShadowRoot);
+</script>
+
+<a href="http://example.org" ondragstart="doDragStartShadowRootImage(event)">
+ <div id="shadow_host_containing_image"></div>
+</a>
+
+<script>
+shadow_host_containing_image.attachShadow({ mode: 'open' }).innerHTML =
+`<img src="${$("image").src}" alt="Alt Text"></img>`;
+</script>
+
+<a href="http://example.org" id="link_user_select_none_child" draggable="false" ondragstart="onDragOverDraggableFalse(event)">
+ <span id="userselectnone" style="user-select: none">
+ This is an unselectable, undraggable area.
+ </span>
+</a>
+
+<div id="draggable_with_undraggable_descendant" draggable="true" ondragstart="onDragStartDraggable(event)">
+ <a id="undraggable_inside_draggable" href="http://example.org" draggable="false" ondragstart="onDragOverDraggableFalse(event)">
+ This is an undraggable link inside a draggable ancestor.
+ </a>
+</div>
+
+<a id="link_to_file" href="file:///" ondragstart="onDragStartUnlinkable(event)">This is an undraggable 'file' link</a>
+<a id="link_to_windows_file" href="c:/Users/" ondragstart="onDragStartUnlinkable(event)">This is an undraggable 'file' link</a>
+<a id="link_to_backslash_windows_file" href="c:\\Users\\" ondragstart="onDragStartUnlinkable(event)">This is an undraggable 'file' link</a>
+<img id="broken_file_image" src="file:///" ondragstart="onDragStartUnlinkable(event)">
+<img id="broken_windows_file_image" src="c:/Users/" ondragstart="onDragStartUnlinkable(event)">
+<img id="broken_backslash_windows_file_image" src="c:\\Users\\" ondragstart="onDragStartUnlinkable(event)">
+</body>
+</html>
diff --git a/dom/events/test/test_error_events.html b/dom/events/test/test_error_events.html
new file mode 100644
index 0000000000..1d47e5398b
--- /dev/null
+++ b/dom/events/test/test_error_events.html
@@ -0,0 +1,71 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for error events being ErrorEvent</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+ setup({allow_uncaught_exception:true});
+ var errorEvent;
+ var file;
+ var line;
+ var msg;
+ var column;
+ var error;
+ window.addEventListener("error", function(e) {
+ errorEvent = e;
+ }, {once: true});
+ var oldOnerror = window.onerror;
+ window.onerror = function(message, filename, lineno, columnno, errorObject) {
+ window.onerror = oldOnerror;
+ file = filename;
+ line = lineno;
+ msg = message;
+ column = columnno;
+ error = errorObject;
+ }
+ var thrown = new Error("hello");
+ throw thrown;
+</script>
+<script>
+ generate_tests(assert_equals, [
+ [ "Event filename", errorEvent.filename, location.href ],
+ [ "Callback filename", file, location.href ],
+ [ "Event line number", errorEvent.lineno, 27 ],
+ [ "Callback line number", line, 27 ],
+ [ "Event message", errorEvent.message, "Error: hello" ],
+ [ "Callback message", msg, "Error: hello" ],
+ [ "Event error-object", errorEvent.error, thrown],
+ [ "Callback error-object", error, thrown ],
+ [ "Event column", errorEvent.colno, 16 ],
+ [ "Callback column", column, 16 ]
+ ]);
+</script>
+<script>
+ var workerLocation = location.protocol + "//" + location.host +
+ location.pathname.replace("test_error_events.html", "error_event_worker.js");
+ var eventFileTest = async_test("Worker event filename");
+ var eventLineTest = async_test("Worker event line number");
+ var eventMessageTest = async_test("Worker event message");
+ var callbackFileTest = async_test("Worker callback filename");
+ var callbackLineTest = async_test("Worker callback line number");
+ var callbackMessageTest = async_test("Worker callback message");
+ var w = new Worker("error_event_worker.js");
+ w.addEventListener("message", function(msg) {
+ if (msg.data.type == "event") {
+ eventFileTest.step(function() { assert_equals(msg.data.filename, workerLocation); });
+ eventFileTest.done();
+ eventLineTest.step(function() { assert_equals(msg.data.lineno, 19); });
+ eventLineTest.done();
+ eventMessageTest.step(function() { assert_equals(msg.data.message, "Error: workerhello"); });
+ eventMessageTest.done();
+ } else {
+ callbackFileTest.step(function() { assert_equals(msg.data.filename, workerLocation); });
+ callbackFileTest.done();
+ callbackLineTest.step(function() { assert_equals(msg.data.lineno, 19); });
+ callbackLineTest.done();
+ callbackMessageTest.step(function() { assert_equals(msg.data.message, "Error: workerhello"); });
+ callbackMessageTest.done();
+ }
+ });
+</script>
diff --git a/dom/events/test/test_eventTimeStamp.html b/dom/events/test/test_eventTimeStamp.html
new file mode 100644
index 0000000000..a0a52409a0
--- /dev/null
+++ b/dom/events/test/test_eventTimeStamp.html
@@ -0,0 +1,116 @@
+<!doctype html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=77992
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Event.timeStamp (Bug 77992)</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=77992">Mozilla Bug 77992</a>
+<p id="display"></p>
+<pre id="test">
+<script type="text/js-worker" id="worker-src">
+ // Simply returns the event timestamp
+ onmessage = function(evt) {
+ postMessage(evt.timeStamp + performance.timeOrigin);
+ }
+</script>
+<script type="text/js-worker" id="shared-worker-src">
+ // Simply returns the event timestamp
+ onconnect = function(evt) {
+ var port = evt.ports[0];
+ port.onmessage = function(messageEvt) {
+ port.postMessage(messageEvt.timeStamp + performance.timeOrigin);
+ };
+ };
+</script>
+<script type="application/javascript">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+
+// This file performs tests that normalize the timeOrigin within a worker
+// and compare it to the page. When this occurs, time can appear to go backwards.
+// This is a known (and accepted) regression of privacy.reduceTimerPrecision so
+// we need to turn it off.
+SpecialPowers.pushPrefEnv({ "set": [
+ ["privacy.reduceTimerPrecision", false]
+ ]}, testRegularEvents);
+
+// Event.timeStamp should be relative to the time origin which is:
+//
+// Non-worker context: navigation start
+// Dedicated worker: navigation start of the document that created the worker
+// Shared worker: creation time of the shared worker
+//
+// See: https://w3c.github.io/web-performance/specs/HighResolutionTime2/Overview.html#sec-time-origin
+
+function testRegularEvents() {
+ var timeBeforeEvent = performance.now();
+ document.getElementById('test').addEventListener("click", function(evt) {
+ var timeAfterEvent = performance.now();
+ ok(evt.timeStamp >= timeBeforeEvent &&
+ evt.timeStamp <= timeAfterEvent,
+ "Event timestamp (" + evt.timeStamp + ") is in expected range: [" +
+ timeBeforeEvent + ", " + timeAfterEvent + "]");
+ testWorkerEvents();
+ });
+ document.getElementById('test').click();
+}
+
+function testWorkerEvents() {
+ var blob = new Blob([ document.getElementById("worker-src").textContent ],
+ { type: "text/javascript" });
+ var worker = new Worker(URL.createObjectURL(blob));
+ worker.onmessage = function(evt) {
+ var timeAfterEvent = performance.now() + performance.timeOrigin;
+ ok(evt.data >= timeBeforeEvent &&
+ evt.data <= timeAfterEvent,
+ "Event timestamp in dedicated worker (" + evt.data +
+ ") is in expected range: [" +
+ timeBeforeEvent + ", " + timeAfterEvent + "]");
+ worker.terminate();
+ testSharedWorkerEvents();
+ };
+ var timeBeforeEvent = performance.now() + performance.timeOrigin;
+ worker.postMessage("");
+}
+
+function testSharedWorkerEvents() {
+ var blob =
+ new Blob([ document.getElementById("shared-worker-src").textContent ],
+ { type: "text/javascript" });
+ // Delay creation of worker slightly so it is easier to distinguish
+ // shared worker creation time from this document's navigation start
+ setTimeout(function() {
+ var timeBeforeEvent = performance.now() + performance.timeOrigin;
+ var worker = new SharedWorker(URL.createObjectURL(blob));
+ worker.port.onmessage = function(evt) {
+ var timeAfterEvent = performance.now() + performance.timeOrigin;
+ ok(evt.data >= timeBeforeEvent &&
+ evt.data <= timeAfterEvent,
+ "Event timestamp in shared worker (" + evt.data +
+ ") is in expected range: [" +
+ timeBeforeEvent + ", " + timeAfterEvent + "]");
+ worker.port.close();
+ finishTests();
+ };
+ worker.port.start();
+ worker.port.postMessage("");
+ }, 500);
+}
+
+var finishTests = function() {
+ SimpleTest.finish();
+};
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_event_handler_cc.html b/dom/events/test/test_event_handler_cc.html
new file mode 100644
index 0000000000..0a4f6bd6f3
--- /dev/null
+++ b/dom/events/test/test_event_handler_cc.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for cycle collection of event handlers</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ function listener() {}
+
+ function make_weak_ref(obj) {
+ let m = new WeakMap;
+ m.set(obj, {});
+ return m;
+ }
+
+ function weak_ref_dead(r) {
+ return !SpecialPowers.nondeterministicGetWeakMapKeys(r).length;
+ }
+
+ function setupTarget1() {
+ let ifr = document.getElementById("target1");
+ let doc = ifr.contentDocument;
+ let b = doc.createElement("button");
+ doc.body.appendChild(b);
+ b.onclick = listener;
+ ifr.remove();
+ return make_weak_ref(b);
+ }
+
+ function setupTarget2() {
+ let ifr = document.getElementById("target2");
+ let doc = ifr.contentDocument;
+ let b = doc.createElement("button");
+ doc.body.appendChild(b);
+ let setFunc = new ifr.contentWindow.Function(`
+ var b = document.querySelector("button");
+ var proto = parent.HTMLElement.prototype;
+ var setter = Object.getOwnPropertyDescriptor(proto, "onclick").set;
+ // Here the current global (and hence CallbackObject global) will be
+ // the parent, the callback will be a known-live thing from the
+ // parent, but the incumbent global will be the child.
+ setter.call(b, parent.listener);
+ `);
+ setFunc();
+ ifr.remove();
+ return make_weak_ref(b);
+ }
+
+ var ref1;
+ var ref2;
+ var maxCounter = 20;
+
+ function GCCCUntilCollected() {
+ SpecialPowers.exactGC(function () {
+ SpecialPowers.forceCC();
+ SpecialPowers.forceGC();
+ SpecialPowers.forceGC();
+
+ if ((!weak_ref_dead(ref1) || !weak_ref_dead(ref2)) && --maxCounter > 0) {
+ requestIdleCallback(GCCCUntilCollected, {timeout: 250});
+ return;
+ }
+
+ ok(weak_ref_dead(ref1),
+ "Should collect cycle through callback global");
+ ok(weak_ref_dead(ref2),
+ "Should collect cycle through incumbent global");
+
+ SimpleTest.finish();
+ });
+ }
+
+ addLoadEvent(function() {
+ ref1 = setupTarget1();
+ ref2 = setupTarget2();
+ GCCCUntilCollected();
+ });
+ </script>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<iframe id="target1"></iframe>
+<iframe id="target2"></iframe>
+<pre id="test"></pre>
+</body>
+</html>
diff --git a/dom/events/test/test_event_screenXY_in_cross_origin_iframe.html b/dom/events/test/test_event_screenXY_in_cross_origin_iframe.html
new file mode 100644
index 0000000000..4cd0dfd358
--- /dev/null
+++ b/dom/events/test/test_event_screenXY_in_cross_origin_iframe.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<iframe width=100></iframe>
+<iframe width=100></iframe>
+<script>
+function getScreenPosition(aElement, aOffsetX, aOffsetY) {
+ const rect = aElement.getBoundingClientRect();
+ const x = aOffsetX + window.mozInnerScreenX + rect.left;
+ const y = aOffsetY + window.mozInnerScreenY + rect.top;
+ const scale = window.devicePixelRatio;
+ return [Math.round(x * scale), Math.round(y * scale)];
+}
+
+function waitForMessage(aMsg, aOrigin) {
+ return new Promise(resolve => {
+ window.addEventListener("message", function listener(event) {
+ if (event.data == "ready" && event.origin == aOrigin) {
+ window.removeEventListener("message", listener);
+ resolve();
+ }
+ });
+ });
+}
+
+add_task(async () => {
+ await SimpleTest.promiseFocus();
+
+ const iframes = document.querySelectorAll("iframe");
+ iframes[0].src = "file_event_screenXY.html";
+ await waitForMessage("ready", location.origin);
+
+ iframes[1].src = "https://example.com/tests/dom/events/test/file_event_screenXY.html";
+ await waitForMessage("ready", "https://example.com");
+
+ // Wait for APZ state stable so that mouse event handling APZ works properly
+ // in out-of-process iframes.
+ await promiseApzFlushedRepaints();
+
+ const promiseForSameOrigin = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ is(event.origin, location.origin, "origin should be the same as parent");
+ resolve(event.data);
+ }, { once: true });
+ });
+
+ // NOTE: synthesizeMouseAtCenter doesn't work for OOP iframes (bug 1528935),
+ // so we use promiseNativeMouseEvent instead.
+ const [expectedScreenXInSameOrigin, expectedScreenYInSameOrigin] =
+ getScreenPosition(iframes[0], 10, 10);
+ await promiseNativeMouseEvent({
+ type: "click",
+ target: iframes[0],
+ screenX: expectedScreenXInSameOrigin,
+ screenY: expectedScreenYInSameOrigin,
+ scale: "inScreenPixels",
+ });
+
+ const eventInSameOrigin = await promiseForSameOrigin;
+ is(eventInSameOrigin.screenX, expectedScreenXInSameOrigin,
+ "event.screenX should be the same");
+ is(eventInSameOrigin.screenY, expectedScreenYInSameOrigin,
+ "event.screenY should be the same");
+
+ const [expectedScreenXInCrossOrigin, expectedScreenYInCrossOrigin] =
+ getScreenPosition(iframes[1], 10, 10);
+ await promiseNativeMouseEvent({
+ type: "click",
+ target: iframes[0],
+ screenX: expectedScreenXInCrossOrigin,
+ screenY: expectedScreenYInCrossOrigin,
+ scale: "inScreenPixels",
+ });
+
+ const promiseForCrossOrigin = new Promise(resolve => {
+ window.addEventListener("message", event => {
+ is(event.origin, "https://example.com", "origin should be example.com");
+ resolve(event.data);
+ }, { once: true });
+ });
+
+ const eventInCrossOrigin = await promiseForCrossOrigin;
+ is(eventInCrossOrigin.screenX, expectedScreenXInCrossOrigin,
+ "even.screenX should be the same");
+ is(eventInCrossOrigin.screenY, expectedScreenYInCrossOrigin,
+ "even.screenY should be the same");
+
+ is(eventInSameOrigin.screenY, eventInCrossOrigin.screenY,
+ "event.screenY in both iframes should be the same");
+ // Sanity checks to make sure client{X,Y} are the same.
+ is(eventInSameOrigin.clientX, eventInCrossOrigin.clientX,
+ "event.clientX in both iframes should be the same");
+ is(eventInSameOrigin.clientY, eventInCrossOrigin.clientY,
+ "event.clientY in both iframes should be the same");
+});
+</script>
diff --git a/dom/events/test/test_event_screenXY_with_zoom.html b/dom/events/test/test_event_screenXY_with_zoom.html
new file mode 100644
index 0000000000..7f276a4534
--- /dev/null
+++ b/dom/events/test/test_event_screenXY_with_zoom.html
@@ -0,0 +1,63 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Event.screenX/Y on a zoomed page.</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ #target {
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ /* We want synthesizeMouseAtCenter to click on the same point regardless of
+ zoom, so we achieve that by centering the target in our viewport */
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ margin: auto;
+ }
+</style>
+<div id="target" style=""></div>
+<script>
+const target = document.getElementById("target");
+async function getScreenPositionOfTarget() {
+ return new Promise(resolve => {
+ target.addEventListener("click", function(e) {
+ resolve({ x: e.screenX, y: e.screenY });
+ }, { once: true });
+ synthesizeMouseAtCenter(target, {});
+ });
+}
+
+function getScreen() {
+ return {
+ width: screen.width,
+ height: screen.height,
+ top: screen.top,
+ left: screen.left,
+ };
+}
+
+add_task(async () => {
+ let pos = await getScreenPositionOfTarget();
+ let s = getScreen();
+
+ SpecialPowers.setFullZoom(window, 2);
+ let zoomedPos = await getScreenPositionOfTarget();
+ let zoomedScreen = getScreen();
+
+ info(`Original pos=${JSON.stringify(pos)} s=${JSON.stringify(s)}`);
+ info(`Zoomed pos=${JSON.stringify(zoomedPos)} s=${JSON.stringify(zoomedScreen)}`);
+
+ isfuzzy(pos.x, zoomedPos.x * 2, 1, "screenX coordinate");
+ isfuzzy(pos.y, zoomedPos.y * 2, 1, "screenY coordinate");
+
+ isfuzzy(s.top, zoomedScreen.top * 2, 1, "Screen.top");
+ isfuzzy(s.left, zoomedScreen.left * 2, 1, "Screen.left");
+ isfuzzy(s.width, zoomedScreen.width * 2, 1, "Screen.width");
+ isfuzzy(s.height, zoomedScreen.height * 2, 1, "Screen.height");
+
+ SpecialPowers.setFullZoom(window, 1);
+});
+</script>
diff --git a/dom/events/test/test_eventctors.html b/dom/events/test/test_eventctors.html
new file mode 100644
index 0000000000..01ae59493e
--- /dev/null
+++ b/dom/events/test/test_eventctors.html
@@ -0,0 +1,930 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=675884
+-->
+<head>
+ <title>Test for Bug 675884</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=675884">Mozilla Bug 675884</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 675884 **/
+
+var receivedEvent;
+document.addEventListener("hello", function(e) { receivedEvent = e; }, true);
+
+function isMethodResultInitializer(aPropName)
+{
+ return aPropName.startsWith("modifier");
+}
+
+function getPropValue(aEvent, aPropName)
+{
+ if (aPropName.startsWith("modifier")) {
+ return aEvent.getModifierState(aPropName.substr("modifier".length));
+ }
+ return aEvent[aPropName];
+}
+
+// Event
+var e;
+var ex = false;
+try {
+ e = new Event();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+try {
+ e = new Event("foo", 123);
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "2nd parameter should be an object!");
+ex = false;
+
+try {
+ e = new Event("foo", "asdf");
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "2nd parameter should be an object!");
+ex = false;
+
+
+try {
+ e = new Event("foo", false);
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "2nd parameter should be an object!");
+ex = false;
+
+
+e = new Event("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+e.isTrusted = true;
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+
+try {
+ e.__defineGetter__("isTrusted", function() { return true });
+} catch (exp) {
+ ex = true;
+}
+ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
+ex = false;
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+
+ok(!("isTrusted" in Object.getPrototypeOf(e)))
+
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+document.dispatchEvent(e);
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+is(receivedEvent, e, "Wrong event!");
+
+e = new Event("hello", null);
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+document.dispatchEvent(e);
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+is(receivedEvent, e, "Wrong event!");
+
+e = new Event("hello", undefined);
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+document.dispatchEvent(e);
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+is(receivedEvent, e, "Wrong event!");
+
+e = new Event("hello", {});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+document.dispatchEvent(e);
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+is(receivedEvent, e, "Wrong event!");
+
+e = new Event("hello", { bubbles: true, cancelable: true });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+// CustomEvent
+
+try {
+ e = new CustomEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+e = new CustomEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new CustomEvent("hello", { bubbles: true, cancelable: true, detail: window });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.detail, window , "Wrong event.detail!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new CustomEvent("hello", { cancelable: true, detail: window });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.detail, window , "Wrong event.detail!");
+
+e = new CustomEvent("hello", { detail: 123 });
+is(e.detail, 123, "Wrong event.detail!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+
+var dict = { get detail() { return document.body } };
+e = new CustomEvent("hello", dict);
+is(e.detail, dict.detail, "Wrong event.detail!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+
+var dict = { get detail() { throw "foo"; } };
+
+try {
+ e = new CustomEvent("hello", dict);
+} catch (exp) {
+ ex = true;
+}
+ok(ex, "Should have thrown an exception!");
+ex = false;
+
+// BlobEvent
+
+try {
+ e = new BlobEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+try {
+ e = new BlobEvent("hello");
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "data attribute is required in init dict");
+ex = false;
+
+var blob = new Blob();
+e = new BlobEvent("hello", {data: blob});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+try {
+ e.__defineGetter__("isTrusted", function() { return true });
+} catch (exp) {
+ ex = true;
+}
+ok(ex, "Shouldn't be able to re-define the getter for isTrusted.");
+ex = false;
+ok(!e.isTrusted, "BlobEvent shouldn't be trusted!");
+
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new BlobEvent("hello", { bubbles: true, cancelable: true, data: blob });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.data, blob , "Wrong event.data!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new BlobEvent("hello", {data: blob});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event should be cancelable1!");
+is(e.data, blob , "Wrong event.data!");
+
+try {
+ e = new BlobEvent("hello", { data: null });
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "data attribute cannot be null");
+ex = false;
+blob = null;
+
+// CloseEvent
+
+try {
+ e = new CloseEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+e = new CloseEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.wasClean, false, "wasClean should be false!");
+is(e.code, 0, "code should be 0!");
+is(e.reason, "", "reason should be ''!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new CloseEvent("hello",
+ { bubbles: true, cancelable: true, wasClean: true, code: 1, reason: "foo" });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.wasClean, true, "wasClean should be true!");
+is(e.code, 1, "code should be 1!");
+is(e.reason, "foo", "reason should be 'foo'!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new CloseEvent("hello",
+ { bubbles: true, cancelable: true, wasClean: true, code: 1 });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.wasClean, true, "wasClean should be true!");
+is(e.code, 1, "code should be 1!");
+is(e.reason, "", "reason should be ''!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+
+// HashChangeEvent
+
+try {
+ e = new HashChangeEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+e = new HashChangeEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.oldURL, "", "oldURL should be ''");
+is(e.newURL, "", "newURL should be ''");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new HashChangeEvent("hello",
+ { bubbles: true, cancelable: true, oldURL: "old", newURL: "new" });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.oldURL, "old", "oldURL should be 'old'");
+is(e.newURL, "new", "newURL should be 'new'");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new HashChangeEvent("hello",
+ { bubbles: true, cancelable: true, newURL: "new" });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.oldURL, "", "oldURL should be ''");
+is(e.newURL, "new", "newURL should be 'new'");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+// InputEvent
+
+e = new InputEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.detail, 0, "detail should be 0");
+ok(!e.isComposing, "isComposing should be false");
+
+e = new InputEvent("hi!", { bubbles: true, detail: 5, isComposing: false });
+is(e.type, "hi!", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.detail, 5, "detail should be 5");
+ok(!e.isComposing, "isComposing should be false");
+
+e = new InputEvent("hi!", { cancelable: true, detail: 0, isComposing: true });
+is(e.type, "hi!", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.detail, 0, "detail should be 0");
+ok(e.isComposing, "isComposing should be true");
+
+// KeyboardEvent
+
+try {
+ e = new KeyboardEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "KeyboardEvent: First parameter is required!");
+ex = false;
+
+e = new KeyboardEvent("hello");
+is(e.type, "hello", "KeyboardEvent: Wrong event type!");
+ok(!e.isTrusted, "KeyboardEvent: Event shouldn't be trusted!");
+ok(!e.bubbles, "KeyboardEvent: Event shouldn't bubble!");
+ok(!e.cancelable, "KeyboardEvent: Event shouldn't be cancelable!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "KeyboardEvent: Wrong event!");
+
+var keyboardEventProps =
+[
+ { bubbles: false },
+ { cancelable: false },
+ { view: null },
+ { detail: 0 },
+ { key: "" },
+ { code: "" },
+ { location: 0 },
+ { ctrlKey: false },
+ { shiftKey: false },
+ { altKey: false },
+ { metaKey: false },
+ { modifierAltGraph: false },
+ { modifierCapsLock: false },
+ { modifierFn: false },
+ { modifierFnLock: false },
+ { modifierNumLock: false },
+ { modifierScrollLock: false },
+ { modifierSymbol: false },
+ { modifierSymbolLock: false },
+ { repeat: false },
+ { isComposing: false },
+ { charCode: 0 },
+ { keyCode: 0 },
+ { which: 0 },
+];
+
+var testKeyboardProps =
+[
+ { bubbles: true },
+ { cancelable: true },
+ { view: window },
+ { detail: 1 },
+ { key: "CustomKey" },
+ { code: "CustomCode" },
+ { location: 1 },
+ { ctrlKey: true },
+ { shiftKey: true },
+ { altKey: true },
+ { metaKey: true },
+ { modifierAltGraph: true },
+ { modifierCapsLock: true },
+ { modifierFn: true },
+ { modifierFnLock: true },
+ { modifierNumLock: true },
+ { modifierScrollLock: true },
+ { modifierSymbol: true },
+ { modifierSymbolLock: true },
+ { repeat: true },
+ { isComposing: true },
+ { charCode: 2 },
+ { keyCode: 3 },
+ { which: 4 },
+ { charCode: 5, which: 6 },
+ { keyCode: 7, which: 8 },
+ { keyCode: 9, charCode: 10 },
+ { keyCode: 11, charCode: 12, which: 13 },
+];
+
+var defaultKeyboardEventValues = {};
+for (var i = 0; i < keyboardEventProps.length; ++i) {
+ for (prop in keyboardEventProps[i]) {
+ if (!isMethodResultInitializer(prop)) {
+ ok(prop in e, "keyboardEvent: KeyboardEvent doesn't have property " + prop + "!");
+ }
+ defaultKeyboardEventValues[prop] = keyboardEventProps[i][prop];
+ }
+}
+
+while (testKeyboardProps.length) {
+ var p = testKeyboardProps.shift();
+ e = new KeyboardEvent("foo", p);
+ for (var def in defaultKeyboardEventValues) {
+ if (!(def in p)) {
+ is(getPropValue(e, def), defaultKeyboardEventValues[def],
+ "KeyboardEvent: Wrong default value for " + def + "!");
+ } else {
+ is(getPropValue(e, def), p[def],
+ "KeyboardEvent: Wrong event init value for " + def + "!");
+ }
+ }
+}
+
+// PageTransitionEvent
+
+try {
+ e = new PageTransitionEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+e = new PageTransitionEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.persisted, false, "persisted should be false");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new PageTransitionEvent("hello",
+ { bubbles: true, cancelable: true, persisted: true});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.persisted, true, "persisted should be true");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new PageTransitionEvent("hello", { persisted: true});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.persisted, true, "persisted should be true");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+// PopStateEvent
+
+try {
+ e = new PopStateEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+e = new PopStateEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.state, null, "persisted should be null");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new PopStateEvent("hello",
+ { bubbles: true, cancelable: true, state: window});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.state, window, "persisted should be window");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+
+e = new PopStateEvent("hello", { state: window});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.state, window, "persisted should be window");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+// UIEvent
+
+try {
+ e = new UIEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+try {
+ e = new UIEvent("foo", { view: {} });
+ e.view.onunload;
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "{} isn't a valid value.");
+ex = false;
+
+try {
+ e = new UIEvent("foo", { view: null });
+} catch(exp) {
+ ex = true;
+}
+ok(!ex, "null is a valid value.");
+is(e.view, null);
+ex = false;
+
+e = new UIEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.detail, 0, "detail should be 0");
+is(e.view, null, "view should be null");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new UIEvent("hello",
+ { bubbles: true, cancelable: true, view: window, detail: 1});
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.detail, 1, "detail should be 1");
+is(e.view, window, "view should be window");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+// StorageEvent
+
+e = document.createEvent("StorageEvent");
+ok(e, "Should have created an event!");
+
+try {
+ e = new StorageEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "First parameter is required!");
+ex = false;
+
+e = new StorageEvent("hello");
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(!e.bubbles, "Event shouldn't bubble!");
+ok(!e.cancelable, "Event shouldn't be cancelable!");
+is(e.key, null, "key should be null");
+is(e.oldValue, null, "oldValue should be null");
+is(e.newValue, null, "newValue should be null");
+is(e.url, "", "url should be ''");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+e = new StorageEvent("hello",
+ { bubbles: true, cancelable: true, key: "key",
+ oldValue: "oldValue", newValue: "newValue", url: "url",
+ storageArea: localStorage });
+is(e.type, "hello", "Wrong event type!");
+ok(!e.isTrusted, "Event shouldn't be trusted!");
+ok(e.bubbles, "Event should bubble!");
+ok(e.cancelable, "Event should be cancelable!");
+is(e.key, "key", "Wrong value");
+is(e.oldValue, "oldValue", "Wrong value");
+is(e.newValue, "newValue", "Wrong value");
+is(e.url, "url", "Wrong value");
+is(e.storageArea, localStorage, "Wrong value");
+document.dispatchEvent(e);
+is(receivedEvent, e, "Wrong event!");
+
+// MouseEvent
+
+try {
+ e = new MouseEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "MouseEvent: First parameter is required!");
+ex = false;
+
+e = new MouseEvent("hello", { buttons: 1, movementX: 2, movementY: 3});
+is(e.type, "hello", "MouseEvent: Wrong event type!");
+ok(!e.isTrusted, "MouseEvent: Event shouldn't be trusted!");
+ok(!e.bubbles, "MouseEvent: Event shouldn't bubble!");
+ok(!e.cancelable, "MouseEvent: Event shouldn't be cancelable!");
+is(e.buttons, 1);
+is(e.movementX, 2);
+is(e.movementY, 3);
+document.dispatchEvent(e);
+is(receivedEvent, e, "MouseEvent: Wrong event!");
+
+var mouseEventProps =
+[ { screenX: 0 },
+ { screenY: 0 },
+ { clientX: 0 },
+ { clientY: 0 },
+ { ctrlKey: false },
+ { shiftKey: false },
+ { altKey: false },
+ { metaKey: false },
+ { modifierAltGraph: false },
+ { modifierCapsLock: false },
+ { modifierFn: false },
+ { modifierFnLock: false },
+ { modifierNumLock: false },
+ { modifierScrollLock: false },
+ { modifierSymbol: false },
+ { modifierSymbolLock: false },
+ { button: 0 },
+ { buttons: 0 },
+ { relatedTarget: null },
+];
+
+var testProps =
+[
+ { screenX: 1 },
+ { screenY: 2 },
+ { clientX: 3 },
+ { clientY: 4 },
+ { ctrlKey: true },
+ { shiftKey: true },
+ { altKey: true },
+ { metaKey: true },
+ { modifierAltGraph: true },
+ { modifierCapsLock: true },
+ { modifierFn: true },
+ { modifierFnLock: true },
+ { modifierNumLock: true },
+ { modifierScrollLock: true },
+ { modifierSymbol: true },
+ { modifierSymbolLock: true },
+ { button: 5 },
+ { buttons: 6 },
+ { relatedTarget: window }
+];
+
+var defaultMouseEventValues = {};
+for (var i = 0; i < mouseEventProps.length; ++i) {
+ for (prop in mouseEventProps[i]) {
+ if (!isMethodResultInitializer(prop)) {
+ ok(prop in e, "MouseEvent: MouseEvent doesn't have property " + prop + "!");
+ }
+ defaultMouseEventValues[prop] = mouseEventProps[i][prop];
+ }
+}
+
+while (testProps.length) {
+ var p = testProps.shift();
+ e = new MouseEvent("foo", p);
+ for (var def in defaultMouseEventValues) {
+ if (!(def in p)) {
+ is(getPropValue(e, def), defaultMouseEventValues[def],
+ "MouseEvent: Wrong default value for " + def + "!");
+ } else {
+ is(getPropValue(e, def), p[def], "MouseEvent: Wrong event init value for " + def + "!");
+ }
+ }
+}
+
+// PopupBlockedEvent
+
+try {
+ e = new PopupBlockedEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "PopupBlockedEvent: First parameter is required!");
+ex = false;
+
+e = new PopupBlockedEvent("hello");
+is(e.type, "hello", "PopupBlockedEvent: Wrong event type!");
+ok(!e.isTrusted, "PopupBlockedEvent: Event shouldn't be trusted!");
+ok(!e.bubbles, "PopupBlockedEvent: Event shouldn't bubble!");
+ok(!e.cancelable, "PopupBlockedEvent: Event shouldn't be cancelable!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "PopupBlockedEvent: Wrong event!");
+
+e = new PopupBlockedEvent("hello",
+ { requestingWindow: window,
+ popupWindowFeatures: "features",
+ popupWindowName: "name"
+ });
+is(e.requestingWindow, window);
+is(e.popupWindowFeatures, "features");
+is(e.popupWindowName, "name");
+
+// WheelEvent
+
+try {
+ e = new WheelEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "WheelEvent: First parameter is required!");
+ex = false;
+
+e = new WheelEvent("hello", { buttons: 1, movementX: 2, movementY: 3});
+is(e.type, "hello", "WheelEvent: Wrong event type!");
+is(e.buttons, 1);
+is(e.movementX, 2);
+is(e.movementY, 3);
+ok(!e.isTrusted, "WheelEvent: Event shouldn't be trusted!");
+ok(!e.bubbles, "WheelEvent: Event shouldn't bubble!");
+ok(!e.cancelable, "WheelEvent: Event shouldn't be cancelable!");
+document.dispatchEvent(e);
+is(receivedEvent, e, "WheelEvent: Wrong event!");
+
+var wheelEventProps =
+[ { screenX: 0 },
+ { screenY: 0 },
+ { clientX: 0 },
+ { clientY: 0 },
+ { ctrlKey: false },
+ { shiftKey: false },
+ { altKey: false },
+ { metaKey: false },
+ { modifierAltGraph: false },
+ { modifierCapsLock: false },
+ { modifierFn: false },
+ { modifierFnLock: false },
+ { modifierNumLock: false },
+ { modifierScrollLock: false },
+ { modifierSymbol: false },
+ { modifierSymbolLock: false },
+ { button: 0 },
+ { buttons: 0 },
+ { relatedTarget: null },
+ { deltaX: 0.0 },
+ { deltaY: 0.0 },
+ { deltaZ: 0.0 },
+ { deltaMode: 0 }
+];
+
+var testWheelProps =
+[
+ { screenX: 1 },
+ { screenY: 2 },
+ { clientX: 3 },
+ { clientY: 4 },
+ { ctrlKey: true },
+ { shiftKey: true },
+ { altKey: true },
+ { metaKey: true },
+ { modifierAltGraph: true },
+ { modifierCapsLock: true },
+ { modifierFn: true },
+ { modifierFnLock: true },
+ { modifierNumLock: true },
+ { modifierScrollLock: true },
+ { modifierSymbol: true },
+ { modifierSymbolLock: true },
+ { button: 5 },
+ { buttons: 6 },
+ { relatedTarget: window },
+ { deltaX: 7.8 },
+ { deltaY: 9.1 },
+ { deltaZ: 2.3 },
+ { deltaMode: 4 }
+];
+
+var defaultWheelEventValues = {};
+for (var i = 0; i < wheelEventProps.length; ++i) {
+ for (prop in wheelEventProps[i]) {
+ if (!isMethodResultInitializer(prop)) {
+ ok(prop in e, "WheelEvent: WheelEvent doesn't have property " + prop + "!");
+ }
+ defaultWheelEventValues[prop] = wheelEventProps[i][prop];
+ }
+}
+
+while (testWheelProps.length) {
+ var p = testWheelProps.shift();
+ e = new WheelEvent("foo", p);
+ for (var def in defaultWheelEventValues) {
+ if (!(def in p)) {
+ is(getPropValue(e, def), defaultWheelEventValues[def],
+ "WheelEvent: Wrong default value for " + def + "!");
+ } else {
+ is(getPropValue(e, def), p[def], "WheelEvent: Wrong event init value for " + def + "!");
+ }
+ }
+}
+
+// DragEvent
+
+try {
+ e = new DragEvent();
+} catch(exp) {
+ ex = true;
+}
+ok(ex, "DragEvent: First parameter is required!");
+ex = false;
+
+e = new DragEvent("hello", { buttons: 1, movementX: 2, movementY: 3});
+is(e.type, "hello", "DragEvent: Wrong event type!");
+is(e.buttons, 1);
+is(e.movementX, 2);
+is(e.movementY, 3);
+document.dispatchEvent(e);
+is(receivedEvent, e, "DragEvent: Wrong event!");
+
+// TransitionEvent
+e = new TransitionEvent("hello", { propertyName: "color", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
+is("propertyName" in e, true, "Transition events have propertyName property");
+is("foobar" in e, false, "Transition events do not copy random properties from event init");
+is(e.propertyName, "color", "Transition event copies propertyName from TransitionEventInit");
+is(e.elapsedTime, 3.5, "Transition event copies elapsedTime from TransitionEventInit");
+is(e.pseudoElement, "", "Transition event copies pseudoElement from TransitionEventInit");
+is(e.bubbles, false, "Lack of bubbles property in TransitionEventInit");
+is(e.cancelable, false, "Lack of cancelable property in TransitionEventInit");
+is(e.type, "hello", "Wrong event type!");
+is(e.isTrusted, false, "Event shouldn't be trusted!");
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+
+// AnimationEvent
+e = new AnimationEvent("hello", { animationName: "bounce3", elapsedTime: 3.5, pseudoElement: "", foobar: "baz" })
+is("animationName" in e, true, "Animation events have animationName property");
+is("foobar" in e, false, "Animation events do not copy random properties from event init");
+is(e.animationName, "bounce3", "Animation event copies animationName from AnimationEventInit");
+is(e.elapsedTime, 3.5, "Animation event copies elapsedTime from AnimationEventInit");
+is(e.pseudoElement, "", "Animation event copies pseudoElement from AnimationEventInit");
+is(e.bubbles, false, "Lack of bubbles property in AnimationEventInit");
+is(e.cancelable, false, "Lack of cancelable property in AnimationEventInit");
+is(e.type, "hello", "Wrong event type!");
+is(e.isTrusted, false, "Event shouldn't be trusted!");
+is(e.eventPhase, Event.NONE, "Wrong event phase");
+
+// InputEvent
+let dataTransfer = new DataTransfer();
+dataTransfer.setData("text/plain", "foo");
+e = new InputEvent("hello", {data: "something data", dataTransfer, inputType: "invalid input type", isComposing: true});
+is(e.type, "hello", "InputEvent should set type attribute");
+is(e.data, "something data", "InputEvent should have data attribute");
+is(e.dataTransfer, dataTransfer, "InputEvent should have the dataTransfer");
+is(e.dataTransfer.getData("text/plain"), "foo", "InputEvent.dataTransfer should keep handling its data");
+try {
+ e.dataTransfer.setData("text/plain", "bar");
+} catch (exp) {
+ ok(false, `InputEvent.dataTransfer.setData("text/plain", "bar") shouldn't fail (${exp})`);
+}
+is(e.dataTransfer.getData("text/plain"), "bar", "InputEvent.dataTransfer should be modified by a call of its setData()");
+is(e.inputType, "invalid input type", "InputEvent should have inputType attribute");
+is(e.isComposing, true, "InputEvent should have isComposing attribute");
+
+dataTransfer = new DataTransfer();
+e = new InputEvent("hello", {data: "", dataTransfer, inputType: "insertText"});
+is(e.data, "", "InputEvent.data should be empty string when empty string is specified explicitly");
+is(e.dataTransfer, dataTransfer, "InputEvent.dataTransfer should have the empty dataTransfer");
+is(e.inputType, "insertText", "InputEvent.inputType should return valid inputType from EditorInputType enum");
+e = new InputEvent("hello", {data: "foo", inputType: "deleteWordBackward"});
+is(e.data, "foo", "InputEvent.data should be the specified string");
+is(e.inputType, "deleteWordBackward", "InputEvent.inputType should return valid inputType from EditorInputType enum");
+e = new InputEvent("hello", {inputType: "formatFontName"});
+is(e.inputType, "formatFontName", "InputEvent.inputType should return valid inputType from EditorInputType enum");
+
+e = new InputEvent("input", {});
+is(e.data, null, "InputEvent.data should be null in default");
+is(e.dataTransfer, null, "InputEvent.dataTransfer should be null in default");
+is(e.inputType, "", "InputEvent.inputType should be empty string in default");
+is(e.isComposing, false, "InputEvent.isComposing should be false in default");
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_eventctors.xhtml b/dom/events/test/test_eventctors.xhtml
new file mode 100644
index 0000000000..7906a3c7a8
--- /dev/null
+++ b/dom/events/test/test_eventctors.xhtml
@@ -0,0 +1,49 @@
+<?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=675884
+-->
+<window title="Mozilla Bug 675884"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+ <!-- 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=675884"
+ target="_blank">Mozilla Bug 675884</a>
+ </body>
+
+ <!-- test code goes here -->
+ <script type="application/javascript">
+ <![CDATA[
+ /** Test for Bug 675884 **/
+
+ // Most of the tests are in .html file, but test here that
+ // isTrusted is handled correctly in chrome.
+
+ var receivedEvent;
+ document.addEventListener("hello", function(e) { receivedEvent = e; }, true);
+
+ // Event
+ var e;
+ var ex = false;
+ try {
+ e = new Event();
+ } catch(exp) {
+ ex = true;
+ }
+ ok(ex, "First parameter is required!");
+ ex = false;
+
+ e = new Event("hello");
+ is(e.type, "hello", "Wrong event type!");
+ ok(e.isTrusted, "Event should be trusted!");
+ ok(!e.bubbles, "Event shouldn't bubble!");
+ ok(!e.cancelable, "Event shouldn't be cancelable!");
+ document.dispatchEvent(e);
+ is(receivedEvent, e, "Wrong event!");
+
+ ]]>
+ </script>
+</window>
diff --git a/dom/events/test/test_eventctors_sensors.html b/dom/events/test/test_eventctors_sensors.html
new file mode 100644
index 0000000000..090b7d62d0
--- /dev/null
+++ b/dom/events/test/test_eventctors_sensors.html
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=675884
+-->
+<head>
+ <title>Test for Bug 675884</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=675884">Mozilla Bug 675884</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+
+SpecialPowers.pushPrefEnv({"set": [
+ ["device.sensors.enabled", true],
+ ["device.sensors.orientation.enabled", true],
+ ["device.sensors.motion.enabled", true],
+ ["device.sensors.proximity.enabled", true],
+ ["device.sensors.ambientLight.enabled", true]
+]}, () => {
+ let receivedEvent;
+ document.addEventListener("hello", function(e) { receivedEvent = e; }, true);
+
+ // UserProximityEvent
+ e = new UserProximityEvent("hello", {near: true});
+ is(e.type, "hello", "Wrong event type!");
+ ok(!e.isTrusted, "Event should not be trusted");
+ is(e.near, true, "near should be true");
+ document.dispatchEvent(e);
+ is(receivedEvent, e, "Wrong event!");
+
+ // DeviceLightEvent
+ e = new DeviceLightEvent("hello", {value: 1} );
+ is(e.type, "hello", "Wrong event type!");
+ ok(!e.isTrusted, "Event should not be trusted");
+ is(e.value, 1, "value should be 1");
+ document.dispatchEvent(e);
+ is(receivedEvent, e, "Wrong event!");
+ e = new DeviceLightEvent("hello", {value: Infinity} );
+ is(e.value, Infinity, "value should be positive infinity");
+ e = new DeviceLightEvent("hello", {value: -Infinity} );
+ is(e.value, -Infinity, "value should be negative infinity");
+ e = new DeviceLightEvent("hello");
+ is(e.value, Infinity, "Uninitialized value should be positive infinity");
+
+ // DeviceOrientationEvent
+ e = new DeviceOrientationEvent("hello");
+ is(e.type, "hello", "Wrong event type!");
+ ok(!e.isTrusted, "Event should not be trusted");
+ is(e.alpha, null);
+ is(e.beta, null);
+ is(e.gamma, null);
+ is(e.absolute, false);
+
+ e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } );
+ is(e.type, "hello", "Wrong event type!");
+ ok(!e.isTrusted, "Event should not be trusted");
+ is(e.alpha, 1);
+ is(e.beta, 2);
+ is(e.gamma, 3);
+ is(e.absolute, true);
+ document.dispatchEvent(e);
+ is(receivedEvent, e, "Wrong event!");
+
+ // DeviceMotionEvent
+ e = new DeviceMotionEvent("hello");
+ is(e.type, "hello", "Wrong event type!");
+ ok(!e.isTrusted, "Event should not be trusted");
+ is(typeof e.acceleration, "object");
+ is(e.acceleration.x, null);
+ is(e.acceleration.y, null);
+ is(e.acceleration.z, null);
+ is(typeof e.accelerationIncludingGravity, "object");
+ is(e.accelerationIncludingGravity.x, null);
+ is(e.accelerationIncludingGravity.y, null);
+ is(e.accelerationIncludingGravity.z, null);
+ is(typeof e.rotationRate, "object");
+ is(e.rotationRate.alpha, null);
+ is(e.rotationRate.beta, null);
+ is(e.rotationRate.gamma, null);
+ is(e.interval, null);
+
+ SimpleTest.finish();
+});
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_eventhandler_scoping.html b/dom/events/test/test_eventhandler_scoping.html
new file mode 100644
index 0000000000..f15238a0c8
--- /dev/null
+++ b/dom/events/test/test_eventhandler_scoping.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for event handler scoping</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+var queryResult;
+test(function() {
+ var d = document.createElement("div");
+ d.setAttribute("onclick", "queryResult = querySelector('span')");
+ var s = document.createElement("span");
+ d.appendChild(s);
+ d.dispatchEvent(new Event("click"));
+ assert_equals(queryResult, s, "Should have gotten the right object");
+}, "Test for bareword calls in an event handler using the element as 'this'");
+</script>
diff --git a/dom/events/test/test_focus_abspos.html b/dom/events/test/test_focus_abspos.html
new file mode 100644
index 0000000000..8798c4ed11
--- /dev/null
+++ b/dom/events/test/test_focus_abspos.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>Test for bug 1424633: clicking on an oof descendant focus its focusable ancestor</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<style>
+ #focusable {
+ width: 100px;
+ height: 100px;
+ background-color: blue;
+ }
+ #oof {
+ background-color: green;
+ position: absolute;
+ top: 25px;
+ }
+</style>
+<div tabindex="0" id="focusable">
+ <span id="oof">Absolute</span>
+</div>
+<script>
+window.onload = function() {
+ async_test(function(t) {
+ document.body.offsetTop;
+ setTimeout(t.step_func_done(function() {
+ let span = document.querySelector("#oof");
+ synthesizeMouseAtCenter(span, {type: "mousedown"});
+ assert_equals(document.activeElement, document.querySelector("#focusable"));
+ }), 0);
+ }, "Clicking on an abspos descendant focus its focusable ancestor");
+}
+</script>
diff --git a/dom/events/test/test_focus_blur_on_click_in_cross_origin_iframe.html b/dom/events/test/test_focus_blur_on_click_in_cross_origin_iframe.html
new file mode 100644
index 0000000000..66d105d4a4
--- /dev/null
+++ b/dom/events/test/test_focus_blur_on_click_in_cross_origin_iframe.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<iframe width=100></iframe>
+<script>
+SimpleTest.requestLongerTimeout(2);
+
+let state = "start";
+
+function getScreenPosition(aElement, aOffsetX, aOffsetY) {
+ const rect = aElement.getBoundingClientRect();
+ const x = aOffsetX + window.mozInnerScreenX + rect.left;
+ const y = aOffsetY + window.mozInnerScreenY + rect.top;
+ const scale = window.devicePixelRatio;
+ return [Math.round(x * scale), Math.round(y * scale)];
+}
+
+add_task(async () => {
+ await SimpleTest.promiseFocus();
+
+ const loadsPromise = new Promise((resolve, reject) => {
+ window.addEventListener("message", function listener(event) {
+ info(`receive ${event.data}`);
+ if (event.data == "ready") {
+ is(state, "start", "check initial state");
+ state = "ready";
+ resolve();
+ } else {
+ reject("Unexpected message");
+ }
+ }, { once: true });
+ });
+
+ const iframe = document.querySelectorAll("iframe")[0];
+ iframe.src = "https://example.com/tests/dom/events/test/file_focus_blur_on_click_in_cross_origin_iframe.html";
+
+ await loadsPromise;
+
+ // Wait for APZ state stable so that mouse event handling APZ works properly
+ // in out-of-process iframes.
+ await waitUntilApzStable();
+
+ // NOTE: synthesizeMouseAtCenter doesn't work for OOP iframes (bug 1528935),
+ // so we use promiseNativeMouseEventWithAPZ instead.
+ const [expectedScreenX, expectedScreenY] =
+ getScreenPosition(iframe, 10, 10);
+
+ const firstClickPromise = new Promise((resolve, reject) => {
+ window.addEventListener("message", function listener(event) {
+ info(`receive ${event.data}`);
+ if (state == "ready") {
+ if (event.data == "focus") {
+ state = "focusbeforeclick";
+ } else if (event.data == "click") {
+ ok(false, "Focusing failed to complete before mouseup");
+ state = "clickbeforefocus";
+ } else {
+ ok(false, "Unexpected event");
+ }
+ } else if (state == "focusbeforeclick") {
+ is(event.data, "click", "The second event should be 'click'");
+ state = "firstclick";
+ window.removeEventListener("message", listener);
+ resolve();
+ } else if (state == "clickbeforefocus") {
+ is(event.data, "focus", "The second event should be 'click'");
+ state = "firstclick";
+ window.removeEventListener("message", listener);
+ resolve();
+ } else {
+ reject("Unexpected message");
+ }
+ });
+ });
+
+ await promiseNativeMouseEventWithAPZ({
+ type: "click",
+ target: iframe,
+ screenX: expectedScreenX,
+ screenY: expectedScreenY,
+ });
+
+ await firstClickPromise;
+
+ SimpleTest.requestFlakyTimeout("Waiting for unwanted events that don't exist on success.");
+
+ const secondClickPromise = new Promise((resolve, reject) => {
+ window.addEventListener("message", function listener(event) {
+ info(`receive ${event.data}`);
+ if (state == "firstclick") {
+ is(event.data, "click", "The third event should be 'click' again, not 'blur' or 'focus'.");
+ state = "secondclick";
+ setTimeout(function() {
+ // Wait for potential other unwanted events
+ window.removeEventListener("message", listener);
+ resolve()
+ }, 200);
+ } else {
+ reject("Unexpected message " + event.data);
+ }
+ });
+ });
+
+ await promiseNativeMouseEventWithAPZ({
+ type: "click",
+ target: iframe,
+ screenX: expectedScreenX,
+ screenY: expectedScreenY,
+ });
+
+ await secondClickPromise;
+});
+</script>
diff --git a/dom/events/test/test_focus_blur_on_click_in_deep_cross_origin_iframe.html b/dom/events/test/test_focus_blur_on_click_in_deep_cross_origin_iframe.html
new file mode 100644
index 0000000000..f360ef7c20
--- /dev/null
+++ b/dom/events/test/test_focus_blur_on_click_in_deep_cross_origin_iframe.html
@@ -0,0 +1,146 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title></title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_native_event_utils.js"></script>
+<iframe width=100 height=200 scrolling="no"></iframe>
+<script>
+let state = "start";
+
+const scale = window.devicePixelRatio;
+
+function getScreenPosition(aElement, aOffsetX, aOffsetY) {
+ const rect = aElement.getBoundingClientRect();
+ const x = aOffsetX + window.mozInnerScreenX + rect.left;
+ const y = aOffsetY + window.mozInnerScreenY + rect.top;
+ return [Math.round(x * scale), Math.round(y * scale)];
+}
+
+add_task(async () => {
+ await SimpleTest.promiseFocus();
+
+ const loadsPromise = new Promise((resolve, reject) => {
+ let readyMiddle = false;
+ let readyInner = false;
+ window.addEventListener("message", function listener(event) {
+ info(`receive ${event.data}`);
+ if (event.data == "middleready") {
+ readyMiddle = true;
+ } else if (event.data == "innerready") {
+ readyInner = true;
+ } else {
+ reject("Unexpected message when waiting for ready " + event.data);
+ }
+ if (readyInner && readyMiddle) {
+ state = "ready";
+ window.removeEventListener("message", listener);
+ resolve();
+ }
+ });
+ });
+
+ const iframe = document.querySelectorAll("iframe")[0];
+ iframe.src = "https://example.com/tests/dom/events/test/file_focus_blur_on_click_in_deep_cross_origin_iframe_middle.html";
+
+ await loadsPromise;
+
+ // Wait for APZ state stable so that mouse event handling APZ works properly
+ // in out-of-process iframes.
+ await waitUntilApzStable();
+
+ // NOTE: synthesizeMouseAtCenter doesn't work for OOP iframes (bug 1528935),
+ // so we use promiseNativeMouseEventWithAPZ instead.
+ const [expectedScreenX, expectedScreenY] =
+ getScreenPosition(iframe, 10, 10);
+
+ const firstClickPromise = new Promise((resolve, reject) => {
+ window.addEventListener("message", function listener(event) {
+ info(`receive ${event.data}`);
+ if (state == "ready") {
+ if (event.data == "innerfocus") {
+ state = "innerfocusbeforeclick";
+ } else if (event.data == "innerclick") {
+ ok(false, "Focusing failed to complete before mouseup");
+ state = "innerclickbeforefocus";
+ } else if (event.data == "middlefocus") {
+ is(false, "Should not get an extra middlefocus.");
+ } else {
+ is(event.data, "neverthisevent", "Unexpected event (first click)");
+ }
+ } else if (state == "innerfocusbeforeclick") {
+ is(event.data, "innerclick", "The second event should be 'innerclick'");
+ state = "firstclick";
+ window.removeEventListener("message", listener);
+ resolve();
+ } else if (state == "innerclickbeforefocus") {
+ is(event.data, "innerfocus", "The second event should be 'innerfocus'");
+ state = "firstclick";
+ window.removeEventListener("message", listener);
+ resolve();
+ } else {
+ reject("Unexpected message in firstClickPromise " + event.data);
+ }
+ });
+ });
+
+ await promiseNativeMouseEventWithAPZ({
+ type: "click",
+ target: iframe,
+ screenX: expectedScreenX,
+ screenY: expectedScreenY + Math.round(110 * scale),
+ });
+
+ await firstClickPromise;
+
+ SimpleTest.requestFlakyTimeout("Waiting for unwanted events that don't exist on success.");
+
+ const secondClickPromise = new Promise((resolve, reject) => {
+ window.addEventListener("message", function listener(event) {
+ info(`receive ${event.data}`);
+ if (state == "firstclick") {
+ is(event.data, "middlefocus", "The third event should be 'middlefocus'.");
+ state = "middlefocusbeforeclick";
+ } else if (state == "middlefocusbeforeclick") {
+ // The order of blur and click is non-deterministic even in the non-Fission case.
+ if (event.data == "middleclick") {
+ state = "waitingforinnerblurafterclick";
+ } else if (event.data == "innerblur") {
+ state = "innerblurbeforeclick";
+ } else {
+ is(event.data, "neverthisevent", "Unexpected event (first click)");
+ }
+ } else if (state == "waitingforinnerblurafterclick") {
+ is(event.data, "innerblur", "The fifth event should be 'innerblur'.");
+ state = "secondclick";
+ setTimeout(function() {
+ // Wait for potential other unwanted events
+ window.removeEventListener("message", listener);
+ resolve()
+ }, 200);
+ } else if (state == "innerblurbeforeclick") {
+ is(event.data, "middleclick", "The fifth event should be 'middleclick'.");
+ state = "secondclick";
+ setTimeout(function() {
+ // Wait for potential other unwanted events
+ window.removeEventListener("message", listener);
+ resolve()
+ }, 200);
+ } else {
+ reject("Unexpected message in secondClickPromise " + event.data);
+ }
+ });
+ });
+
+ await promiseNativeMouseEventWithAPZ({
+ type: "click",
+ target: iframe,
+ screenX: expectedScreenX,
+ screenY: expectedScreenY,
+ });
+
+ await secondClickPromise;
+});
+</script>
diff --git a/dom/events/test/test_hover_mouseleave.html b/dom/events/test/test_hover_mouseleave.html
new file mode 100644
index 0000000000..aac25f5b51
--- /dev/null
+++ b/dom/events/test/test_hover_mouseleave.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test :hover state on mouseleave.</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<style>
+div {
+ width: 100px;
+ height: 100px;
+}
+</style>
+<div id="target" style="background: green;"></div>
+<div id="outside" style="background: blue;"></div>
+<script>
+SimpleTest.waitForExplicitFinish();
+let mouseLeaveCount = 0;
+let mouseOutCount = 0;
+
+target.addEventListener("mouseleave", () => {
+ if (mouseLeaveCount++ != 0)
+ return;
+ is(target.matches(":hover"), false,
+ "Should've been not hovered on mouseleave");
+ is(outside.matches(":hover"), true,
+ "New target should be hovered on mouseleave");
+ if (mouseOutCount)
+ SimpleTest.finish();
+});
+
+target.addEventListener("mouseout", () => {
+ if (mouseOutCount++ != 0)
+ return;
+ is(target.matches(":hover"), false,
+ "Should've been not hovered on mouseleave");
+ is(outside.matches(":hover"), true,
+ "New target should be hovered on mouseleave");
+ if (mouseLeaveCount)
+ SimpleTest.finish();
+});
+
+SimpleTest.waitForFocus(() => {
+ synthesizeMouseAtCenter(outside, { type: "mousemove" });
+ synthesizeMouseAtCenter(target, { type: "mousemove" });
+ synthesizeMouseAtCenter(outside, { type: "mousemove" });
+});
+</script>
diff --git a/dom/events/test/test_legacy_event.html b/dom/events/test/test_legacy_event.html
new file mode 100644
index 0000000000..82ab2dec6b
--- /dev/null
+++ b/dom/events/test/test_legacy_event.html
@@ -0,0 +1,297 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1236979
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1236979 (events that have legacy alternative versions)</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ <style>
+ @keyframes anim1 {
+ 0% { margin-left: 0px }
+ 100% { margin-left: 100px }
+ }
+ </style>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1236979">Mozilla Bug 1236979</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Bug 1236979 **/
+
+'use strict';
+SimpleTest.waitForExplicitFinish();
+
+// Array of info-bundles about each legacy event to be tested:
+var gLegacyEventInfo = [
+ {
+ legacy_name: "webkitTransitionEnd",
+ modern_name: "transitionend",
+ trigger_event: triggerShortTransition,
+ },
+ {
+ legacy_name: "webkitAnimationStart",
+ modern_name: "animationstart",
+ trigger_event: triggerShortAnimation,
+ },
+ {
+ legacy_name: "webkitAnimationEnd",
+ modern_name: "animationend",
+ trigger_event: triggerShortAnimation,
+ },
+ {
+ legacy_name: "webkitAnimationIteration",
+ modern_name: "animationiteration",
+ trigger_event: triggerAnimationIteration,
+ }
+];
+
+// EVENT-TRIGGERING FUNCTIONS
+// --------------------------
+// This function triggers a very short (1ms long) transition, which will cause
+// events to fire for the transition ending.
+function triggerShortTransition(node) {
+ node.style.transition = "1ms color linear" ;
+ node.style.color = "purple";
+ // Flush style, so that the above assignment value actually takes effect
+ // in the computed style, so that a transition will get triggered when it
+ // changes.
+ window.getComputedStyle(node).color;
+ node.style.color = "teal";
+}
+
+// This function triggers a very short (1ms long) animation, which will cause
+// events to fire for the animation beginning & ending.
+function triggerShortAnimation(node) {
+ node.style.animation = "anim1 1ms linear";
+}
+
+// This function triggers a very short (10ms long) animation with many
+// iterations, which will cause a start event followed by an iteration event
+// on each subsequent tick, to fire.
+//
+// NOTE: We need the many iterations since if an animation frame coincides
+// with the animation starting or ending we dispatch only the start or end
+// event and not the iteration event.
+function triggerAnimationIteration(node) {
+ node.style.animation = "anim1 10ms linear 20000";
+}
+
+// GENERAL UTILITY FUNCTIONS
+// -------------------------
+// Creates a new div and appends it as a child of the specified parentNode, or
+// (if no parent is specified) as a child of the element with ID 'display'.
+function createChildDiv(parentNode) {
+ if (!parentNode) {
+ parentNode = document.getElementById("display");
+ if (!parentNode) {
+ ok(false, "no 'display' element to append to");
+ }
+ }
+ var div = document.createElement("div");
+ parentNode.appendChild(div);
+ return div;
+}
+
+// Returns an event-handler function, which (when invoked) simply checks that
+// the event's type matches what's expected. If a callback is passed in, then
+// the event-handler will invoke that callback as well.
+function createHandlerWithTypeCheck(expectedEventType, extraHandlerLogic) {
+ var handler = function(e) {
+ is(e.type, expectedEventType,
+ "When an event handler for '" + expectedEventType + "' is invoked, " +
+ "the event's type field should be '" + expectedEventType + "'.");
+ if (extraHandlerLogic) {
+ extraHandlerLogic(e);
+ }
+ }
+ return handler;
+}
+
+// TEST FUNCTIONS
+// --------------
+// These functions expect to be passed an entry from gEventInfo, and they
+// return a Promise which performs the test & resolves when it's complete.
+// The function names all begin with "mp", which stands for "make promise".
+// So e.g. "mpTestLegacyEventSent" means "make a promise to test that the
+// legacy event is sent".
+
+// Tests that the legacy event type is sent, when only a legacy handler is
+// registered.
+function mpTestLegacyEventSent(eventInfo) {
+ return new Promise(
+ function(resolve, reject) {
+ // Create a node & register an event-handler for the legacy event:
+ var div = createChildDiv();
+
+ var handler = createHandlerWithTypeCheck(eventInfo.legacy_name,
+ function() {
+ // When event-handler is done, clean up & resolve:
+ div.remove();
+ resolve();
+ });
+ div.addEventListener(eventInfo.legacy_name, handler);
+
+ // Trigger the event:
+ eventInfo.trigger_event(div);
+ }
+ );
+}
+
+// Test that the modern event type (and only the modern event type) is fired,
+// when listeners of both modern & legacy types are registered. The legacy
+// listener should not be invoked.
+function mpTestModernBeatsLegacy(eventInfo) {
+ return new Promise(
+ function(resolve, reject) {
+ var div = createChildDiv();
+
+ var legacyHandler = function(e) {
+ reject("Handler for legacy event '" + eventInfo.legacy_name +
+ "' should not be invoked when there's a handler registered " +
+ "for both modern & legacy event type on the same node");
+ };
+
+ var modernHandler = createHandlerWithTypeCheck(eventInfo.modern_name,
+ function() {
+ // Indicate that the test has passed (we invoked the modern handler):
+ ok(true, "Handler for modern event '" + eventInfo.modern_name +
+ "' should be invoked when there's a handler registered for " +
+ "both modern & legacy event type on the same node");
+ // When event-handler is done, clean up & resolve:
+ div.remove();
+ resolve();
+ });
+
+ div.addEventListener(eventInfo.legacy_name, legacyHandler);
+ div.addEventListener(eventInfo.modern_name, modernHandler);
+ eventInfo.trigger_event(div);
+ }
+ );
+}
+
+// Test that an event which bubbles may fire listeners of different flavors
+// (modern vs. legacy) at each bubbling level, depending on what's registered
+// at that level.
+function mpTestDiffListenersEventBubbling(eventInfo) {
+ return new Promise(
+ function(resolve, reject) {
+ var grandparent = createChildDiv();
+ var parent = createChildDiv(grandparent);
+ var target = createChildDiv(parent);
+ var didEventFireOnTarget = false;
+ var didEventFireOnParent = false;
+ var eventSentToTarget;
+
+ target.addEventListener(eventInfo.modern_name,
+ createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
+ ok(e.bubbles, "Expecting event to bubble");
+ eventSentToTarget = e;
+ didEventFireOnTarget = true;
+ }));
+
+ parent.addEventListener(eventInfo.legacy_name,
+ createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) {
+ is(e, eventSentToTarget,
+ "Same event object should bubble, despite difference in type");
+ didEventFireOnParent = true;
+ }));
+
+ grandparent.addEventListener(eventInfo.modern_name,
+ createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
+ ok(didEventFireOnTarget,
+ "Event should have fired on child");
+ ok(didEventFireOnParent,
+ "Event should have fired on parent");
+ is(e, eventSentToTarget,
+ "Same event object should bubble, despite difference in type");
+ // Clean up.
+ grandparent.remove();
+ resolve();
+ }));
+
+ eventInfo.trigger_event(target);
+ }
+ );
+}
+
+// Test that an event in the capture phase may fire listeners of different
+// flavors (modern vs. legacy) at each level, depending on what's registered
+// at that level.
+function mpTestDiffListenersEventCapturing(eventInfo) {
+ return new Promise(
+ function(resolve, reject) {
+ var grandparent = createChildDiv();
+ var parent = createChildDiv(grandparent);
+ var target = createChildDiv(parent);
+ var didEventFireOnTarget = false;
+ var didEventFireOnParent = false;
+ var didEventFireOnGrandparent = false;
+ var eventSentToGrandparent;
+
+ grandparent.addEventListener(eventInfo.modern_name,
+ createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
+ eventSentToGrandparent = e;
+ didEventFireOnGrandparent = true;
+ }), true);
+
+ parent.addEventListener(eventInfo.legacy_name,
+ createHandlerWithTypeCheck(eventInfo.legacy_name, function(e) {
+ is(e.eventPhase, Event.CAPTURING_PHASE,
+ "event should be in capturing phase");
+ is(e, eventSentToGrandparent,
+ "Same event object should capture, despite difference in type");
+ ok(didEventFireOnGrandparent,
+ "Event should have fired on grandparent");
+ didEventFireOnParent = true;
+ }), true);
+
+ target.addEventListener(eventInfo.modern_name,
+ createHandlerWithTypeCheck(eventInfo.modern_name, function(e) {
+ is(e.eventPhase, Event.AT_TARGET,
+ "event should be at target phase");
+ is(e, eventSentToGrandparent,
+ "Same event object should capture, despite difference in type");
+ ok(didEventFireOnParent,
+ "Event should have fired on parent");
+ // Clean up.
+ grandparent.remove();
+ resolve();
+ }), true);
+
+ eventInfo.trigger_event(target);
+ }
+ );
+}
+
+// MAIN FUNCTION: Kick off the tests.
+function main() {
+ Promise.resolve().then(function() {
+ return Promise.all(gLegacyEventInfo.map(mpTestLegacyEventSent))
+ }).then(function() {
+ return Promise.all(gLegacyEventInfo.map(mpTestModernBeatsLegacy));
+ }).then(function() {
+ return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventCapturing));
+ }).then(function() {
+ return Promise.all(gLegacyEventInfo.map(mpTestDiffListenersEventBubbling));
+ }).then(function() {
+ SimpleTest.finish();
+ }).catch(function(reason) {
+ ok(false, "Test failed: " + reason);
+ SimpleTest.finish();
+ });
+}
+
+main();
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_legacy_touch_api.html b/dom/events/test/test_legacy_touch_api.html
new file mode 100644
index 0000000000..610f787586
--- /dev/null
+++ b/dom/events/test/test_legacy_touch_api.html
@@ -0,0 +1,65 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1412485
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1412485</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 1412485 **/
+ SimpleTest.waitForExplicitFinish();
+
+ function testExistenceOfLegacyTouchAPIs(win, enabled) {
+ try {
+ var event = document.createEvent("TouchEvent");
+ ok(event instanceof TouchEvent, "Should be able to create TouchEvent using createEvent.");
+ } catch(ex) {
+ ok(true, "Shouldn't be able create TouchEvent using createEvent.");
+ }
+
+ var targets = [win, win.document, win.document.body];
+ for (target of targets) {
+ is("ontouchstart" in target, enabled, `ontouchstart on target [${enabled}].`);
+ is("ontouchend" in target, enabled, `ontouchend on target [${enabled}].`);
+ is("ontouchmove" in target, enabled, `ontouchmove on target [${enabled}].`);
+ is("ontouchcancel" in target, enabled, `ontouchcancel on target [${enabled}].`);
+ }
+
+ is("createTouch" in win.document, enabled, `createTouch on Document [${enabled}].`);
+ is("createTouchList" in win.document, enabled, `createTouchList on Document [${enabled}].`);
+ }
+
+ function test() {
+ // Test the defaults.
+ testExistenceOfLegacyTouchAPIs(window,
+ navigator.userAgent.includes("Android"));
+
+ // Test explicitly enabling touch APIs.
+ SpecialPowers.pushPrefEnv({"set": [["dom.w3c_touch_events.enabled", 1],
+ ["dom.w3c_touch_events.legacy_apis.enabled", true]]},
+ function() {
+ var iframe = document.createElement("iframe");
+ document.body.appendChild(iframe);
+ iframe.onload = function() {
+ testExistenceOfLegacyTouchAPIs(iframe.contentWindow, true);
+ SimpleTest.finish();
+ }
+ });
+ }
+
+ </script>
+</head>
+<body onload="test()">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1412485">Mozilla Bug 1412485</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_marquee_events.html b/dom/events/test/test_marquee_events.html
new file mode 100644
index 0000000000..22d0eafdf1
--- /dev/null
+++ b/dom/events/test/test_marquee_events.html
@@ -0,0 +1,31 @@
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Test for bug 1425874</title>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+</head>
+<body>
+ <script>
+ var wasEventCalled;
+ function callEventWithAttributeHandler(element, evt) {
+ wasEventCalled = false;
+ let el = document.createElement(element);
+ el.setAttribute(`on${evt}`, "wasEventCalled = true");
+ el.dispatchEvent(new Event(evt));
+ return wasEventCalled;
+ }
+
+ info("Make sure the EventNameType_HTMLMarqueeOnly events only compile for marquee");
+
+ ok(!callEventWithAttributeHandler("div", "bounce"), "no onbounce for div");
+ ok(!callEventWithAttributeHandler("div", "finish"), "no onfinish for div");
+ ok(!callEventWithAttributeHandler("div", "start"), "no onstart for div");
+
+ ok(callEventWithAttributeHandler("marquee", "bounce"), "onbounce for marquee");
+ ok(callEventWithAttributeHandler("marquee", "finish"), "onfinish for marquee");
+ ok(callEventWithAttributeHandler("marquee", "start"), "onstart for marquee");
+ </script>
+</body>
+</html>
diff --git a/dom/events/test/test_messageEvent.html b/dom/events/test/test_messageEvent.html
new file mode 100644
index 0000000000..ac12bbc99b
--- /dev/null
+++ b/dom/events/test/test_messageEvent.html
@@ -0,0 +1,79 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=848294
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 848294</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"/>
+</head>
+<body>
+ <script type="application/javascript">
+ function testMessageEvent(e, test) {
+ ok(e, "MessageEvent created");
+ is(e.type, 'message', 'MessageEvent.type is right');
+
+ is(e.data, 'data' in test ? test.data : null, 'MessageEvent.data is ok');
+ is(e.origin, 'origin' in test ? test.origin : '', 'MessageEvent.origin is ok');
+ is(e.lastEventId, 'lastEventId' in test ? test.lastEventId : '', 'MessageEvent.lastEventId is ok');
+ is(e.source, 'source' in test ? test.source : null, 'MessageEvent.source is ok');
+
+ if (test.ports != undefined) {
+ is(e.ports.length, test.ports.length, 'MessageEvent.ports is ok');
+ is(e.ports, e.ports, 'MessageEvent.ports is ok');
+ } else {
+ ok(!('ports' in test) || test.ports == null, 'MessageEvent.ports is ok');
+ }
+ }
+
+ function runTest() {
+ var channel = new MessageChannel();
+
+ var tests = [
+ {},
+ { data: 42 },
+ { data: {} },
+ { data: true, origin: 'wow' },
+ { data: [], lastEventId: 'wow2' },
+ { data: null, source: null },
+ { data: window, source: window },
+ { data: window, source: channel.port1 },
+ { data: window, source: channel.port1, ports: [ channel.port1, channel.port2 ] },
+ { data: null, ports: [] },
+ ];
+
+ while (tests.length) {
+ var test = tests.shift();
+
+ var e = new MessageEvent('message', test);
+ testMessageEvent(e, test);
+
+ e = new MessageEvent('message');
+ e.initMessageEvent('message', true, true,
+ 'data' in test ? test.data : null,
+ 'origin' in test ? test.origin : '',
+ 'lastEventId' in test ? test.lastEventId : '',
+ 'source' in test ? test.source : null,
+ 'ports' in test ? test.ports : []);
+ testMessageEvent(e, test);
+ }
+
+ try {
+ var e = new MessageEvent('foobar', { source: 42 });
+ ok(false, "Source has to be a window or a port");
+ } catch(ex) {
+ ok(true, "Source has to be a window or a port");
+ }
+
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ runTest();
+ </script>
+</body>
+</html>
diff --git a/dom/events/test/test_messageEvent_init.html b/dom/events/test/test_messageEvent_init.html
new file mode 100644
index 0000000000..9f5eea8f37
--- /dev/null
+++ b/dom/events/test/test_messageEvent_init.html
@@ -0,0 +1,25 @@
+<html><head>
+<title>Test for bug 1308956</title>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+
+<body>
+ <script>
+
+var a = new MessageEvent("message")
+ok(!!a, "We have a MessageEvent");
+is(a.ports.length, 0, "By default MessageEvent.ports is an empty array");
+
+a.initMessageEvent("message", true, false, {}, window.location.href, "", null, []);
+ok(Array.isArray(a.ports), "After InitMessageEvent() we have an array");
+is(a.ports.length, 0, "Length is 0");
+
+var mc = new MessageChannel();
+a.initMessageEvent("message", true, false, {}, window.location.href, "", null, [mc.port1]);
+ok(Array.isArray(a.ports), "After InitMessageEvent() we have an array");
+is(a.ports.length, 1, "Length is 1");
+
+ </script>
+</body>
+</html>
diff --git a/dom/events/test/test_mouse_capture_iframe.html b/dom/events/test/test_mouse_capture_iframe.html
new file mode 100644
index 0000000000..424e73795d
--- /dev/null
+++ b/dom/events/test/test_mouse_capture_iframe.html
@@ -0,0 +1,70 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test mouse capture for iframe</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<style>
+#target {
+ width: 150px;
+ height: 150px;
+}
+</style>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1680405">Mozilla Bug 1680405</a>
+<iframe id="target" frameborder="0" scrolling="no"></iframe>
+<script>
+
+function waitForMessage(aEventType) {
+ return new Promise(function(aResolve, aReject) {
+ window.addEventListener("message", function listener(aEvent) {
+ is(aEvent.data, aEventType, `check received message ${aEvent.data}`);
+ aResolve();
+ }, { once: true });
+ });
+}
+
+let iframe = document.getElementById("target");
+
+add_setup(async function() {
+ await SimpleTest.promiseFocus();
+ await SpecialPowers.pushPrefEnv({ set: [["test.events.async.enabled", true]] });
+ disableNonTestMouseEvents(true);
+ SimpleTest.registerCleanupFunction(() => {
+ disableNonTestMouseEvents(false);
+ });
+
+ iframe.src = "http://example.com/tests/dom/events/test/file_empty.html";
+ await waitForMessage("ready");
+
+ await SpecialPowers.spawn(iframe, [], () => {
+ let handler = function(e) {
+ content.parent.postMessage(e.type, "*");
+ };
+ content.document.addEventListener("mousedown", handler);
+ content.document.addEventListener("mousemove", handler);
+ content.document.addEventListener("mouseup", handler);
+ });
+
+ await waitUntilApzStable();
+});
+
+add_task(async function testMouseCaptureOnXoriginIframe() {
+ let unexpectedHandler = function(e) {
+ ok(false, `receive unexpected ${e.type} event`);
+ };
+ document.addEventListener("mousedown", unexpectedHandler);
+ document.addEventListener("mousemove", unexpectedHandler);
+ document.addEventListener("mouseup", unexpectedHandler);
+
+ synthesizeMouseAtCenter(iframe, { type: "mousedown" });
+ await waitForMessage("mousedown");
+
+ synthesizeMouse(iframe, 200, 200, { type: "mousemove" });
+ await waitForMessage("mousemove");
+
+ synthesizeMouse(iframe, 200, 200, { type: "mouseup" });
+ await waitForMessage("mouseup");
+});
+</script>
diff --git a/dom/events/test/test_mouse_enterleave_iframe.html b/dom/events/test/test_mouse_enterleave_iframe.html
new file mode 100644
index 0000000000..52d944ecca
--- /dev/null
+++ b/dom/events/test/test_mouse_enterleave_iframe.html
@@ -0,0 +1,362 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Test mouseenter and mouseleave for iframe.</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/paint_listener.js"></script>
+<script src="/tests/gfx/layers/apz/test/mochitest/apz_test_utils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+<style>
+#start {
+ width: 300px;
+ height: 30px;
+}
+
+#target, #target2 {
+ width: 150px;
+ height: 150px;
+ background-color: #fcc;
+ display: inline-block;
+}
+
+#frame, #frame2 {
+ height: 100%;
+ width: 100%;
+}
+
+#reflow, #div {
+ width: 300px;
+ height: 10px;
+ background-color: lightgreen;
+}
+</style>
+<div id="start">Start from here!!</div>
+<div id="div"></div>
+<div id="target">
+ <iframe id="frame" frameborder="0" scrolling="no"></iframe>
+</div>
+<div id="target2">
+ <iframe id="frame2" frameborder="0" scrolling="no"></iframe>
+</div>
+<div id="reflow"></div>
+<script>
+
+function reflow() {
+ let div = document.getElementById("reflow");
+ div.style.display = "none";
+ div.getBoundingClientRect();
+ div.style.display = "block";
+ div.getBoundingClientRect();
+}
+
+function waitForMessage(aRemoteTarget, aEventType, aTargetName, aLastExpectedElement) {
+ return new Promise(function (aResolve, aReject) {
+ const data = `waiting for "${aEventType}" on <${aTargetName}> in <iframe id="${aRemoteTarget.id}">`;
+ let expectedMessageReceived = false;
+ window.addEventListener("message", function listener(aEvent) {
+ if (aEvent.source != aRemoteTarget.contentWindow) {
+ return;
+ }
+
+ if (aEvent.data.eventType == "reflowed") {
+ if (expectedMessageReceived) {
+ window.removeEventListener("message", listener);
+ aResolve();
+ ok(true, `Message listener ${data} is correctly removed`);
+ }
+ return;
+ }
+
+ if (aEvent.data.eventType !== aEventType) {
+ window.removeEventListener("message", listener);
+ is(
+ aEvent.data.eventType,
+ aEventType,
+ `receive unexpected message ${JSON.stringify(aEvent.data)} at ${data}`
+ );
+ aReject(new Error(`receive unexpected message ${JSON.stringify(aEvent.data)} at ${data}`));
+ return;
+ }
+
+ if (aEvent.data.targetName !== aTargetName) {
+ return;
+ }
+
+ if (expectedMessageReceived) {
+ window.removeEventListener("message", listener);
+ ok(false, `receive redundant message at ${data}`);
+ aReject(new Error(`receive redundant message at ${data}`));
+ return;
+ }
+
+ expectedMessageReceived = true;
+ ok(true, `receive message at ${data}`);
+ if (aLastExpectedElement) {
+ // Trigger a reflow which will generate synthesized mouse move event.
+ aRemoteTarget.contentWindow.postMessage("reflow", "*");
+ }
+ });
+ });
+}
+
+/**
+ * Wait for "mouseenter" events in a child document.
+ *
+ * @param aRemoteTarget An <iframe> element which has the child document.
+ * @param aTargetNames An array of `mouseenter` targets which you want to
+ * listen to. The order should be ancestor to descendant.
+ */
+function waitForMouseEnterMessages(aRemoteTarget, aTargetNames) {
+ let promises = [];
+ let targetName;
+ while ((targetName = aTargetNames.shift())) {
+ promises.push(
+ waitForMessage(aRemoteTarget, "mouseenter", targetName, !aTargetNames.length)
+ );
+ }
+ return Promise.all(promises);
+}
+
+/**
+ * Wait for "mouseleave" events in a child document.
+ *
+ * @param aRemoteTarget An <iframe> element which has the child document.
+ * @param aTargetNames An array of `mouseleave` targets which you want to
+ * listen to. The order should be ancestor to descendant.
+ */
+function waitForMouseLeaveMessages(aRemoteTarget, aTargetNames) {
+ let promises = [];
+ let targetName;
+ while ((targetName = aTargetNames.pop())) {
+ promises.push(
+ waitForMessage(aRemoteTarget, "mouseleave", targetName, !aTargetNames.length)
+ );
+ }
+ return Promise.all(promises);
+}
+
+function waitForLeaveEvent(aTarget) {
+ return new Promise(function(aResolve) {
+ aTarget.addEventListener("mouseleave", function(aEvent) {
+ ok(true, `receive ${aEvent.type}`);
+ aResolve();
+ }, { once: true });
+ });
+}
+
+function waitForEnterLeaveEvents(aEnterTarget, aLeaveTarget) {
+ let expectedEvents = [{target: aEnterTarget, eventName: "mouseenter"}];
+ if (aLeaveTarget) {
+ expectedEvents.push({target: aLeaveTarget, eventName: "mouseleave"})
+ }
+
+ return new Promise(function(aResolve, aReject) {
+ function cleanup() {
+ aEnterTarget.removeEventListener("mouseenter", listener);
+ aEnterTarget.removeEventListener("mouseleave", unexpectedEvent);
+ if (aLeaveTarget) {
+ aLeaveTarget.removeEventListener("mouseenter", unexpectedEvent);
+ aLeaveTarget.removeEventListener("mouseleave", listener);
+ }
+ }
+
+ function unexpectedEvent(aEvent) {
+ cleanup();
+ ok(false, `receive unexpected ${aEvent.type}`);
+ aReject(new Error(`receive unexpected ${aEvent.type}`));
+ }
+
+ async function listener(aEvent) {
+ if (expectedEvents.length <= 0) {
+ unexpectedEvent(aEvent);
+ return;
+ }
+
+ let expectedEvent = expectedEvents.pop();
+ if (expectedEvent.target == aEvent.target &&
+ expectedEvent.eventName == aEvent.type) {
+ ok(true, `receive ${aEvent.type}`);
+ } else {
+ unexpectedEvent(aEvent);
+ return;
+ }
+
+ if (!expectedEvents.length) {
+ // Trigger a reflow which will generate synthesized mouse move event.
+ reflow();
+ // Now wait a bit to see if there is any unexpected event fired.
+ setTimeout(function() {
+ cleanup();
+ aResolve();
+ }, 0);
+ }
+ }
+
+ aEnterTarget.addEventListener("mouseenter", listener);
+ aEnterTarget.addEventListener("mouseleave", unexpectedEvent);
+ if (aLeaveTarget) {
+ aLeaveTarget.addEventListener("mouseenter", unexpectedEvent);
+ aLeaveTarget.addEventListener("mouseleave", listener);
+ }
+ });
+}
+
+function moveMouseToInitialPosition() {
+ info("Mouse moves to initial position");
+ return promiseNativeMouseEvent({
+ type: "mousemove",
+ target: document.getElementById("start"),
+ atCenter: true,
+ });
+}
+
+add_setup(async function() {
+ // Wait for focus before starting tests.
+ await SimpleTest.promiseFocus();
+
+ // Wait for apz getting stable.
+ await waitUntilApzStable();
+
+ // Move mouse to initial position.
+ await moveMouseToInitialPosition();
+
+ // After initializing the mouse cursor position, we should load <iframe>s.
+ // This avoids the case that the cursor is over one of them.
+ info("Load child documents into the iframes");
+ let promiseLoadingIFrames = [];
+ for (const iframe of document.querySelectorAll("iframe")) {
+ promiseLoadingIFrames.push(
+ new Promise(resolve => { iframe.addEventListener("load", resolve, {once: true}); })
+ );
+ iframe.src = "http://example.com/tests/dom/events/test/file_mouse_enterleave.html";
+ }
+ await Promise.all(promiseLoadingIFrames);
+});
+
+add_task(async function testMouseEnterLeave() {
+ let div = document.getElementById("div");
+ let target = document.getElementById("target");
+ let iframe = document.getElementById("frame");
+
+ info("Mouse moves to the div above iframe");
+ let promise = waitForEnterLeaveEvents(div);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target: div,
+ atCenter: true,
+ });
+ await promise;
+
+ info("Mouse moves into iframe");
+ promise = Promise.all([waitForEnterLeaveEvents(target, div),
+ waitForMouseEnterMessages(iframe, ["html", "div"])]);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target,
+ atCenter: true,
+ });
+ await promise;
+
+ info("Mouse moves out from iframe to the div above iframe");
+ promise = Promise.all([waitForEnterLeaveEvents(div, target),
+ waitForMouseLeaveMessages(iframe, ["html", "div"])]);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target: div,
+ atCenter: true,
+ });
+ await promise;
+
+ // Move mouse back to initial position. This is to prevent unexpected
+ // mouseleave event in initial steps for test-verify which runs same test
+ // multiple times.
+ await moveMouseToInitialPosition();
+});
+
+add_task(async function testMouseEnterLeaveBetweenIframe() {
+ let target = document.getElementById("target");
+ let iframe = document.getElementById("frame");
+
+ info("Mouse moves into the first iframe");
+ let promise = Promise.all([waitForEnterLeaveEvents(target),
+ waitForMouseEnterMessages(iframe, ["html", "div"])]);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target,
+ atCenter: true,
+ });
+ await promise;
+
+ let target2 = document.getElementById("target2");
+ let iframe2 = document.getElementById("frame2");
+
+ info("Mouse moves out from the first iframe to the second iframe");
+ promise = Promise.all([waitForEnterLeaveEvents(target2, target),
+ waitForMouseLeaveMessages(iframe, ["html", "div"]),
+ waitForMouseEnterMessages(iframe2, ["html", "div"])]);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target: target2,
+ atCenter: true,
+ })
+ await promise;
+
+ info("Mouse moves out from the second iframe to the first iframe");
+ promise = Promise.all([waitForEnterLeaveEvents(target, target2),
+ waitForMouseLeaveMessages(iframe2, ["html", "div"]),
+ waitForMouseEnterMessages(iframe, ["html", "div"])]);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target,
+ atCenter: true,
+ });
+ await promise;
+
+ // Move mouse back to initial position.
+ await Promise.all([waitForLeaveEvent(target),
+ waitForMouseLeaveMessages(iframe, ["html", "div"]),
+ moveMouseToInitialPosition()]);
+});
+
+add_task(async function testMouseEnterLeaveSwitchWindow() {
+ let target = document.getElementById("target");
+ let iframe = document.getElementById("frame");
+
+ info("Mouse moves into iframe");
+ let promise = Promise.all([waitForEnterLeaveEvents(target),
+ waitForMouseEnterMessages(iframe, ["html", "div"])]);
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target,
+ atCenter: true,
+ });
+ await promise;
+
+ info("Open and switch to new window");
+ promise = Promise.all([waitForLeaveEvent(target),
+ waitForMouseLeaveMessages(iframe, ["html", "div"])]);
+ let win = window.open("http://example.com/tests/dom/events/test/file_mouse_enterleave.html");
+ // Trigger a reflow which will generate synthesized mouse move event.
+ win.postMessage("reflow", "*");
+ await promise;
+
+ info("Switch back to test window");
+ promise = Promise.all([waitForEnterLeaveEvents(target),
+ waitForMouseEnterMessages(iframe, ["html", "div"])]);
+ win.close();
+ // Trigger a reflow which will generate synthesized mouse move event.
+ reflow();
+ // Wait for apz getting stable.
+ await waitUntilApzStable();
+ synthesizeNativeMouseEvent({
+ type: "mousemove",
+ target,
+ atCenter: true,
+ });
+ await promise;
+
+ // Move mouse back to initial position.
+ await Promise.all([waitForLeaveEvent(target),
+ moveMouseToInitialPosition()]);
+});
+</script>
diff --git a/dom/events/test/test_mouse_over_at_removing_down_target.html b/dom/events/test/test_mouse_over_at_removing_down_target.html
new file mode 100644
index 0000000000..c5e001b9f1
--- /dev/null
+++ b/dom/events/test/test_mouse_over_at_removing_down_target.html
@@ -0,0 +1,75 @@
+<!doctype html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Check whether `mouseup` events are fired after pending boundary 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>
+div#parent {
+ width: 100%;
+ height: 50px;
+ background-color: gray;
+}
+div#child {
+ width: 100%;
+ height: 40px;
+ background-color: lime;
+}
+</style>
+<script>
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ await SpecialPowers.pushPrefEnv({ set: [["layout.reflow.synthMouseMove", true]] });
+ const winUtils = SpecialPowers.wrap(window).windowUtils;
+ try {
+ winUtils.disableNonTestMouseEvents(true);
+ const parent = document.querySelector("div");
+ const child = parent.querySelector("div");
+ synthesizeMouseAtCenter(child, { type: "mousemove" });
+ await new Promise(resolve => requestAnimationFrame(
+ () => requestAnimationFrame(resolve)
+ ));
+
+ const mouseEvents = [];
+ child.addEventListener("mousedown", event => {
+ event.target.remove();
+ mouseEvents.push("mousedown@div#child");
+ });
+ document.addEventListener("mouseover", event => {
+ mouseEvents.push(`mouseover@${event.target.localName}${event.target.id ? `#${event.target.id}` : ""}`);
+ }, {capture: true});
+ document.addEventListener("mouseup", event => {
+ mouseEvents.push(`mouseup@${event.target.localName}${event.target.id ? `#${event.target.id}` : ""}`);
+ }, {capture: true});
+ winUtils.advanceTimeAndRefresh(100);
+ // Click in the child, then, the child will be removed by the "mousedown"
+ // event listener and that should cause "mouseover" on the parent and that
+ // must be fired before "mouseup".
+ synthesizeMouseAtCenter(child, {});
+ winUtils.restoreNormalRefresh();
+ await new Promise(resolve => requestAnimationFrame(
+ () => requestAnimationFrame(resolve)
+ ));
+ is(
+ mouseEvents.toString(),
+ "mousedown@div#child,mouseover@div#parent,mouseup@div#parent",
+ "mouseover should be fired before mouseup on the ex-parent of the removed child"
+ );
+ } finally {
+ winUtils.disableNonTestMouseEvents(false);
+ document.querySelector("style").remove();
+ }
+ SimpleTest.finish();
+});
+</script>
+</head>
+<body>
+<div id="parent">
+ <div id="child"></div>
+</div>
+</body>
+</html>
diff --git a/dom/events/test/test_moving_and_expanding_selection_per_page.html b/dom/events/test/test_moving_and_expanding_selection_per_page.html
new file mode 100644
index 0000000000..cc4786235d
--- /dev/null
+++ b/dom/events/test/test_moving_and_expanding_selection_per_page.html
@@ -0,0 +1,353 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for expanding selection per page</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>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(() => {
+ open("window_empty_document.html", "_blank", "width=500,height=500");
+});
+
+async function doTests(aWindow) {
+ // On macOS, there is no shortcut keys to extend selection per page.
+ // Therefore, we need to use nsISelectionController.pageMove() instead.
+ const kUseKeyboardEvent = !navigator.platform.includes("Mac");
+ let selectionController = SpecialPowers.wrap(aWindow)
+ .docShell
+ .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
+ .getInterface(SpecialPowers.Ci.nsISelectionDisplay)
+ .QueryInterface(SpecialPowers.Ci.nsISelectionController);
+ // On Windows, per-page selection to start or end expands selection to same
+ // column of first or last line. On the other platforms, it expands selection
+ // to start or end of first or last line.
+ const kSelectToStartOrEnd = !navigator.platform.includes("Win");
+
+ await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]});
+ await SimpleTest.promiseFocus(aWindow);
+
+ function getNodeDescription(aNode) {
+ function getElementDescription(aElement) {
+ if (aElement.getAttribute("id") !== null) {
+ return `${aElement.tagName.toLowerCase()}#${aElement.getAttribute("id")}`;
+ }
+ if (aElement.tagName === "BR") {
+ return `${getElementDescription(aElement.previousSibling)} + br`;
+ }
+ return aElement.tagName.toLowerCase();
+ }
+ switch (aNode.nodeType) {
+ case aNode.TEXT_NODE:
+ return `text node in ${getElementDescription(aNode.parentElement)}`;
+ case aNode.ELEMENT_NODE:
+ return getElementDescription(aNode);
+ default:
+ return "unknown node";
+ }
+ }
+
+ function doTest(aExpandSelection) {
+ // Note that when neither editor has focus nor in caret mode, key navigation
+ // does not call nsISelectionController::PageMove(). Therefore, in such
+ // cases, you need to call doPageDown() and doPageUp() with setting true
+ // to aUseSelectionController.
+ function doPageDown(aUseSelectionController) {
+ if (kUseKeyboardEvent && !aUseSelectionController) {
+ synthesizeKey("KEY_PageDown", {shiftKey: aExpandSelection}, aWindow);
+ } else {
+ selectionController.pageMove(true, aExpandSelection);
+ }
+ }
+
+ function doPageUp(aUseSelectionController) {
+ if (kUseKeyboardEvent && !aUseSelectionController) {
+ synthesizeKey("KEY_PageUp", {shiftKey: aExpandSelection}, aWindow);
+ } else {
+ selectionController.pageMove(false, aExpandSelection);
+ }
+ }
+
+ let doc = aWindow.document;
+ let body = doc.body;
+ let selection = doc.getSelection();
+ let container;
+
+ body.innerHTML = '<span id="s1">first line</span><br>' +
+ '<span id="s2">second line</span><br>' +
+ '<span id="s3">last line</span>';
+ container = doc.documentElement;
+
+ let description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in non-scrollable body: `;
+ is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
+ selection.collapse(doc.getElementById("s1").firstChild, 3);
+ doPageDown(!aExpandSelection);
+ is(container.scrollTop, 0, description + "this test shouldn't create scrollable document");
+ let range = selection.getRangeAt(0);
+ if (aExpandSelection) {
+ is(range.startContainer, doc.getElementById("s1").firstChild,
+ `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
+ is(range.startOffset, 3,
+ `${description} selection should be expanded from the first line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+ is(range.endContainer, doc.getElementById("s3").firstChild,
+ `${description} selection should be expanded into the last line (got: ${getNodeDescription(range.endContainer)})`);
+ if (kSelectToStartOrEnd) {
+ is(range.endOffset, range.endContainer.length,
+ `${description} selection should be expanded to end of the last line`);
+ } else {
+ isfuzzy(range.endOffset, 3, 2,
+ `${description} selection should be expanded to around the last line's 3rd insertion point`);
+ }
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in non-scrollable body: `;
+ selection.collapse(doc.getElementById("s3").firstChild, 3);
+ doPageUp(!aExpandSelection);
+ is(container.scrollTop, 0, description + "this test shouldn't create scrollable document");
+ range = selection.getRangeAt(0);
+ is(range.startContainer, doc.getElementById("s1").firstChild,
+ `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
+ if (kSelectToStartOrEnd) {
+ is(range.startOffset, 0,
+ `${description} selection should be expanded to start of the first line`);
+ } else {
+ isfuzzy(range.startOffset, 3, 2,
+ `${description} selection should be expanded to around the first line's 3rd insertion point`);
+ }
+ if (aExpandSelection) {
+ is(range.endContainer, doc.getElementById("s3").firstChild,
+ `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`);
+ is(range.endOffset, 3,
+ `${description} selection should be expanded from the last line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+
+ body.innerHTML = '<span id="s1">first line in the body</span>' +
+ '<div id="d1" style="height: 2em; line-height: 1em; overflow: auto;">' +
+ '<span id="s2">first line</span><br>' +
+ '<span id="s3">second line</span><br>' +
+ '<span id="s4">third line</span><br>' +
+ '<span id="s5">last line</span>' +
+ "</div>" +
+ '<span id="s6">last line in the body</span>';
+ container = doc.getElementById("d1");
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable area in the body: `;
+ is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
+ selection.collapse(doc.getElementById("s2").firstChild, 3);
+ doPageDown(!aExpandSelection);
+ isnot(container.scrollTop, 0, description + "should be scrolled down");
+ range = selection.getRangeAt(0);
+ if (aExpandSelection) {
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
+ is(range.startOffset, 3,
+ `${description} selection should be expanded from the first line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+ is(range.endContainer, doc.getElementById("s4").firstChild,
+ `${description} selection should be expanded into the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
+ isfuzzy(range.endOffset, 3, 2,
+ `${description} selection should be expanded to around the 3rd line's 3rd insertion point`);
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable area in the body: `;
+ selection.collapse(doc.getElementById("s4").firstChild, 3);
+ let previousScrollTop = container.scrollTop;
+ doPageUp(!aExpandSelection);
+ ok(container.scrollTop < previousScrollTop, description + "should be scrolled up");
+ range = selection.getRangeAt(0);
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
+ isfuzzy(range.startOffset, 3, 2,
+ `${description} selection should be expanded to around the first line's 3rd insertion point`);
+ if (aExpandSelection) {
+ is(range.endContainer, doc.getElementById("s4").firstChild,
+ `${description} selection should be expanded from the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
+ is(range.endOffset, 3,
+ `${description} selection should be expanded from the 3rd line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+
+ body.innerHTML = '<span id="s1">first line in the body</span>' +
+ '<div id="d1" contenteditable style="height: 2em; line-height: 1em; overflow: auto;">' +
+ '<span id="s2">first line</span><br>' +
+ '<span id="s3">second line</span><br>' +
+ '<span id="s4">third line</span><br>' +
+ '<span id="s5">last line</span>' +
+ "</div>" +
+ '<span id="s6">last line in the body</span>';
+ container = doc.getElementById("d1");
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable editable div in the body: `;
+ is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
+ selection.collapse(doc.getElementById("s2").firstChild, 3);
+ doPageDown();
+ isnot(container.scrollTop, 0, description + "should be scrolled down");
+ range = selection.getRangeAt(0);
+ if (aExpandSelection) {
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
+ is(range.startOffset, 3,
+ `${description} selection should be expanded from the first line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+ is(range.endContainer, doc.getElementById("s4").firstChild,
+ `${description} selection should be expanded into the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
+ isfuzzy(range.endOffset, 3, 2,
+ `${description} selection should be expanded to around the 3rd line's 3rd insertion point`);
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable editable div in the body: `;
+ selection.collapse(doc.getElementById("s4").firstChild, 3);
+ previousScrollTop = container.scrollTop;
+ doPageUp();
+ ok(container.scrollTop < previousScrollTop, description + "should be scrolled up");
+ range = selection.getRangeAt(0);
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
+ isfuzzy(range.startOffset, 3, 2,
+ `${description} selection should be expanded to around the first line's 3rd insertion point`);
+ if (aExpandSelection) {
+ is(range.endContainer, doc.getElementById("s4").firstChild,
+ `${description} selection should be expanded from the 3rd line (got: ${getNodeDescription(range.endContainer)})`);
+ is(range.endOffset, 3,
+ `${description} selection should be expanded from the 3rd line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+
+ body.innerHTML = '<span id="s1">first line in the body</span>' +
+ '<div id="d1" contenteditable>' +
+ '<span id="s2">first line</span><br>' +
+ '<span id="s3">second line</span><br>' +
+ '<span id="s4">third line</span><br>' +
+ '<span id="s5">last line</span>' +
+ "</div>" +
+ '<span id="s6">last line in the body</span>';
+ container = doc.getElementById("d1");
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in non-scrollable editable div in the body: `;
+ is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
+ selection.collapse(doc.getElementById("s2").firstChild, 3);
+ doPageDown();
+ is(container.scrollTop, 0, description + "editable div shouldn't be scrollable");
+ range = selection.getRangeAt(0);
+ if (aExpandSelection) {
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
+ is(range.startOffset, 3,
+ `${description} selection should be expanded from the first line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+ is(range.endContainer, doc.getElementById("s5").firstChild,
+ `${description} selection should be expanded into the last line (got: ${getNodeDescription(range.endContainer)})`);
+ if (kSelectToStartOrEnd) {
+ is(range.endOffset, range.endContainer.length,
+ `${description} selection should be expanded to end of the last line`);
+ } else {
+ isfuzzy(range.endOffset, 3, 2,
+ `${description} selection should be expanded to around the last line's 3rd insertion point`);
+ }
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in non-scrollable editable div in the body: `;
+ selection.collapse(doc.getElementById("s5").firstChild, 3);
+ doPageUp();
+ is(container.scrollTop, 0, description + "editable div shouldn't be scrollable");
+ range = selection.getRangeAt(0);
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded into the first line (got: ${getNodeDescription(range.startContainer)})`);
+ if (kSelectToStartOrEnd) {
+ is(range.startOffset, 0,
+ `${description} selection should be expanded to start of the first line`);
+ } else {
+ isfuzzy(range.startOffset, 3, 2,
+ `${description} selection should be expanded to around the first line's 3rd insertion point`);
+ }
+ if (aExpandSelection) {
+ is(range.endContainer, doc.getElementById("s5").firstChild,
+ `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`);
+ is(range.endOffset, 3,
+ `${description} selection should be expanded from the last line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+
+ body.innerHTML = '<span id="s1">first line in the body</span>' +
+ '<div id="d1" contenteditable>' +
+ '<span id="s2">first editable line</span><br>' +
+ '<div id="d2" style="height: 3em; line-height: 1em; overflow: auto;">' +
+ '<span id="s3">first line</span><br>' +
+ '<span id="s4">second line</span>' +
+ "</div>" +
+ '<span id="s5">last editable line</span>' +
+ "</div>" +
+ '<span id="s6">last line in the body</span>';
+ container = doc.getElementById("d2");
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to forward in scrollable div (but not scrollable along y-axis) in the editable div: `;
+ is(container.scrollTop, 0, description + "scrollTop should be 0 at initialization");
+ selection.collapse(doc.getElementById("s3").firstChild, 3);
+ doPageDown();
+ is(container.scrollTop, 0, description + "scrollable div in the editable div (but not scrollable along y-axis) shouldn't be scrollable");
+ range = selection.getRangeAt(0);
+ if (aExpandSelection) {
+ is(range.startContainer, doc.getElementById("s3").firstChild,
+ `${description} selection should be expanded from the first line (got: ${getNodeDescription(range.startContainer)})`);
+ is(range.startOffset, 3,
+ `${description} selection should be expanded from the first line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+ is(range.endContainer, doc.getElementById("s5").firstChild,
+ `${description} selection should be expanded into the last editable line (got: ${getNodeDescription(range.endContainer)})`);
+ if (kSelectToStartOrEnd) {
+ is(range.endOffset, range.endContainer.length,
+ `${description} selection should be expanded to end of the last editable line`);
+ } else {
+ isfuzzy(range.endOffset, 3, 2,
+ `${description} selection should be expanded to around the last editable line's 3rd insertion point`);
+ }
+
+ description = `${aExpandSelection ? "Expanding selection" : "Moving caret"} to backward in scrollable div (but not scrollable along y-axis) in the editable div: `;
+ selection.collapse(doc.getElementById("s4").firstChild, 3);
+ doPageUp();
+ is(container.scrollTop, 0, description + "scrollable div (but not scrollable along y-axis) in the editable div shouldn't be scrollable");
+ range = selection.getRangeAt(0);
+ is(range.startContainer, doc.getElementById("s2").firstChild,
+ `${description} selection should be expanded into the first editable line (got: ${getNodeDescription(range.startContainer)})`);
+ if (kSelectToStartOrEnd) {
+ is(range.startOffset, 0,
+ `${description} selection should be expanded to start of the first editable line`);
+ } else {
+ isfuzzy(range.startOffset, 3, 2,
+ `${description} selection should be expanded to around the first editable line's 3rd insertion point`);
+ }
+ if (aExpandSelection) {
+ is(range.endContainer, doc.getElementById("s4").firstChild,
+ `${description} selection should be expanded from the last line (got: ${getNodeDescription(range.endContainer)})`);
+ is(range.endOffset, 3,
+ `${description} selection should be expanded from the last line's 3rd insertion point`);
+ } else {
+ ok(range.collapsed, `${description} selection should be collapsed`);
+ }
+ }
+
+ doTest(false);
+ doTest(true);
+
+ aWindow.close();
+ SimpleTest.finish();
+}
+</script>
+</html>
diff --git a/dom/events/test/test_moz_mouse_pixel_scroll_event.html b/dom/events/test/test_moz_mouse_pixel_scroll_event.html
new file mode 100644
index 0000000000..4e9aac57c1
--- /dev/null
+++ b/dom/events/test/test_moz_mouse_pixel_scroll_event.html
@@ -0,0 +1,478 @@
+<!DOCTYPE HTML>
+<html id="root">
+<head>
+ <title>Test for MozMousePixelScroll events</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css">
+ <style>
+ html, body { margin: 0 }
+
+ .scrollable {
+ overflow: auto;
+ line-height: 1;
+ margin: 15px;
+ box-sizing: border-box;
+ }
+ .scrollable > div {
+ width: 1000px;
+ height: 1000px;
+ font-size: 1000px;
+ line-height: 1;
+ }
+ /* Ensure viewport is scrollable */
+ .viewport-padding { height: 200vh; width: 200vh; }
+ </style>
+</head>
+<body>
+<p id="display"></p>
+<div id="Scrollable128" class="scrollable" style="font-size: 128px; width: 100px; height: 100px;">
+ <div>
+ <div id="Scrollable96" class="scrollable" style="font-size: 96px; width: 150px; height: 150px;">
+ <div>
+ <div id="Scrollable64" class="scrollable" style="font-size: 64px; width: 200px; height: 200px;">
+ <div>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</div>
+<div id="Scrollable32" class="scrollable" style="font-size: 32px; width: 50px; height: 50px;">
+ <div>
+ </div>
+</div>
+<div class="viewport-padding"></div>
+<div id="content" style="display: none"></div>
+<pre id="test">
+<script>
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(startTest, window);
+
+var gScrollable128 = document.getElementById("Scrollable128");
+var gScrollable96 = document.getElementById("Scrollable96");
+var gScrollable64 = document.getElementById("Scrollable64");
+var gScrollable32 = document.getElementById("Scrollable32");
+var gRoot = document.documentElement;
+
+// TODO(emilio): page scrolling seems to interact weirdly with
+// Android (probably due to the visual viewport? but I can't repro
+// on the emulator). It's probably not a big deal though since you
+// can't usually do page scrolling on Android, and chances are
+// actual behavior is correct and we just need to tweak the test.
+const kIsAndroid = navigator.userAgent.includes("Android");
+
+function* prepareScrollUnits()
+{
+ var result = -1;
+ function handler(aEvent)
+ {
+ result = aEvent.detail;
+ aEvent.preventDefault();
+ setTimeout(runTest, 0);
+ }
+ window.addEventListener("MozMousePixelScroll", handler, { capture: true, passive: false });
+
+ yield waitForAllPaints(function () { setTimeout(runTest, 0); });
+
+ yield synthesizeWheel(gScrollable128, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable128.wheelLineHeight = result;
+ ok(result > 96 && result < 200, "prepareScrollUnits: gScrollable128.wheelLineHeight may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable96, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable96.wheelLineHeight = result;
+ ok(result > 64 && result < gScrollable128.wheelLineHeight, "prepareScrollUnits: gScrollable96.wheelLineHeight may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable64, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable64.wheelLineHeight = result;
+ ok(result > 32 && result < gScrollable96.wheelLineHeight, "prepareScrollUnits: gScrollable64.wheelLineHeight may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable32, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable32.wheelLineHeight = result;
+ ok(result > 16 && result < gScrollable64.wheelLineHeight, "prepareScrollUnits: gScrollable32.wheelLineHeight may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gRoot, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gRoot.wheelLineHeight = result;
+ ok(result > 10 && result < gScrollable32.wheelLineHeight, "prepareScrollUnits: gRoot.wheelLineHeight may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable128, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable128.wheelHorizontalLine = result;
+ ok(result > 50 && result < 200, "prepareScrollUnits: gScrollable128.wheelHorizontalLine may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable96, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable96.wheelHorizontalLine = result;
+ ok(result > 30 && result < gScrollable128.wheelHorizontalLine, "prepareScrollUnits: gScrollable96.wheelHorizontalLine may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable64, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable64.wheelHorizontalLine = result;
+ ok(result > 20 && result < gScrollable96.wheelHorizontalLine, "prepareScrollUnits: gScrollable64.wheelHorizontalLine may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable32, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable32.wheelHorizontalLine = result;
+ ok(result > 12 && result < gScrollable64.wheelHorizontalLine, "prepareScrollUnits: gScrollable32.wheelHorizontalLine may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gRoot, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gRoot.wheelHorizontalLine = result;
+ ok(result > 5 && result < gScrollable32.wheelHorizontalLine, "prepareScrollUnits: gRoot.wheelHorizontalLine may be illegal value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable128, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable128.wheelPageHeight = result;
+ ok(result >= (100 - gScrollable128.wheelLineHeight * 2) && result <= 100,
+ "prepareScrollUnits: gScrollable128.wheelPageHeight is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable96, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable96.wheelPageHeight = result;
+ ok(result >= (150 - gScrollable96.wheelLineHeight * 2) && result <= 150,
+ "prepareScrollUnits: gScrollable96.wheelPageHeight is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable64, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable64.wheelPageHeight = result;
+ ok(result >= (200 - gScrollable64.wheelLineHeight * 2) && result <= 200,
+ "prepareScrollUnits: gScrollable64.wheelPageHeight is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable32, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gScrollable32.wheelPageHeight = result;
+ ok(result >= (50 - gScrollable32.wheelLineHeight * 2) && result <= 50,
+ "prepareScrollUnits: gScrollable32.wheelPageHeight is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gRoot, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaY: 1.0, lineOrPageDeltaY: 1 });
+ gRoot.wheelPageHeight = result;
+ if (!kIsAndroid) {
+ ok(window.innerHeight - result < 100 && window.innerHeight - result > 0,
+ "prepareScrollUnits: gRoot.wheelPageHeight is strange value, got " + result);
+ }
+
+ result = -1;
+ yield synthesizeWheel(gScrollable128, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable128.wheelPageWidth = result;
+ ok(result >= (100 - gScrollable128.wheelLineHeight * 2) && result <= 100,
+ "prepareScrollUnits: gScrollable128.wheelPageWidth is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable96, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable96.wheelPageWidth = result;
+ ok(result >= (150 - gScrollable96.wheelLineHeight * 2) && result <= 150,
+ "prepareScrollUnits: gScrollable96.wheelPageWidth is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable64, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable64.wheelPageWidth = result;
+ ok(result >= (200 - gScrollable64.wheelLineHeight * 2) && result <= 200,
+ "prepareScrollUnits: gScrollable64.wheelPageWidth is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gScrollable32, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gScrollable32.wheelPageWidth = result;
+ ok(result >= (50 - gScrollable32.wheelLineHeight * 2) && result <= 50,
+ "prepareScrollUnits: gScrollable32.wheelPageWidth is strange value, got " + result);
+
+ result = -1;
+ yield synthesizeWheel(gRoot, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, lineOrPageDeltaX: 1 });
+ gRoot.wheelPageWidth = result;
+ if (!kIsAndroid) {
+ ok(window.innerWidth - result < 100 && window.innerWidth - result > 0,
+ "prepareScrollUnits: gRoot.wheelPageWidth is strange value, got " + result);
+ }
+
+ window.removeEventListener("MozMousePixelScroll", handler, true);
+}
+
+function* doTests()
+{
+ let tests = [];
+
+ {
+ const kAllScrollables = [gRoot, gScrollable128, gScrollable96, gScrollable64, gScrollable32];
+ function resetAllScrollables() {
+ for (let scrollable of kAllScrollables) {
+ scrollable.style.overflow = "";
+ scrollable.scrollLeft = 0;
+ scrollable.scrollTop = 0;
+ }
+ }
+
+ function closestScrollable(s) {
+ if (s == gScrollable32 || s == gRoot) {
+ return gRoot;
+ }
+ let closest = kAllScrollables[kAllScrollables.indexOf(s) - 1];
+ ok(closest, `Should find a closest scrollable for ${s.id}`);
+ return closest;
+ }
+
+ const kAllScrollDirections = [
+ {
+ direction: "both",
+ x: 1,
+ y: 1,
+ },
+ {
+ direction: "horizontal",
+ x: 1,
+ y: 0,
+ },
+ {
+ direction: "vertical",
+ x: 0,
+ y: 1,
+ },
+ ];
+
+ for (let deltaMode of [WheelEvent.DOM_DELTA_LINE, WheelEvent.DOM_DELTA_PAGE]) {
+ if (deltaMode == WheelEvent.DOM_DELTA_PAGE && kIsAndroid) {
+ // See comment around kIsAndroid.
+ continue;
+ }
+
+ for (let scrollable of kAllScrollables) {
+ for (let { x, y, direction } of kAllScrollDirections) {
+ tests.push({
+ description: `Should be computed from nearest scrollable element (${scrollable.id}, ${deltaMode}, ${direction})`,
+ target: scrollable,
+ event: {
+ deltaMode,
+ deltaX: x,
+ deltaY: y,
+ lineOrPageDeltaX: x,
+ lineOrPageDeltaY: y,
+ },
+ prepare () {
+ resetAllScrollables();
+ },
+ cleanup () {
+ },
+ expected: {
+ x: x ? scrollable : null,
+ y: y ? scrollable : null,
+ }
+ });
+
+ tests.push({
+ description: `Should be computed from actual scroll target or root if not scrollable in the direction (${scrollable.id}, ${deltaMode}, ${direction})`,
+ target: scrollable,
+ event: {
+ deltaMode,
+ deltaX: -x,
+ deltaY: -y,
+ lineOrPageDeltaX: -x,
+ lineOrPageDeltaY: -y
+ },
+ prepare () {
+ resetAllScrollables();
+ },
+ cleanup () {
+ },
+ expected: {
+ x: x ? gRoot : null,
+ y: y ? gRoot : null,
+ }
+ });
+
+ // Root's page delta depends on whether there's an horizontal scrollbar
+ // or not, so this test on the root would be off by a few pixels.
+ if (scrollable != gRoot || deltaMode != WheelEvent.DOM_DELTA_PAGE) {
+ tests.push({
+ description: `Should be computed from actual scroll target or root if scrollable in the direction but overflow: hidden (${scrollable.id}, ${deltaMode}, ${direction})`,
+ target: scrollable,
+ event: {
+ deltaMode,
+ deltaX: x,
+ deltaY: y,
+ lineOrPageDeltaX: x,
+ lineOrPageDeltaY: y,
+ },
+ prepare () {
+ resetAllScrollables();
+ scrollable.style.overflow = "hidden";
+ },
+ cleanup () {
+ scrollable.style.overflow = "";
+ },
+ expected: {
+ x: x ? closestScrollable(scrollable) : null,
+ y: y ? closestScrollable(scrollable) : null,
+ }
+ });
+ }
+ }
+ }
+ }
+ }
+
+ var currentTest, description, firedX, firedY;
+ var expectedHandlerCalls;
+
+ function handler(aEvent)
+ {
+ aEvent.preventDefault();
+
+ if (aEvent.axis != MouseScrollEvent.HORIZONTAL_AXIS &&
+ aEvent.axis != MouseScrollEvent.VERTICAL_AXIS) {
+ ok(false,
+ description + "The event had invalid axis (" + aEvent.axis + ")");
+ if (--expectedHandlerCalls == 0) {
+ setTimeout(runTest, 0);
+ }
+ return;
+ }
+
+ var isHorizontal = (aEvent.axis == MouseScrollEvent.HORIZONTAL_AXIS);
+ if ((isHorizontal && !currentTest.expected.x) ||
+ (!isHorizontal && !currentTest.expected.y)) {
+ ok(false,
+ description + "The event fired unexpectedly (" +
+ (isHorizontal ? "Horizontal" : "Vertical") + ")");
+ if (--expectedHandlerCalls == 0) {
+ setTimeout(runTest, 0);
+ }
+ return;
+ }
+
+ if (isHorizontal) {
+ firedX = true;
+ } else {
+ firedY = true;
+ }
+
+ var expectedDetail =
+ (currentTest.event.deltaMode == WheelEvent.DOM_DELTA_LINE) ?
+ (isHorizontal ? currentTest.expected.x.wheelHorizontalLine :
+ currentTest.expected.y.wheelLineHeight) :
+ (isHorizontal ? currentTest.expected.x.wheelPageWidth :
+ currentTest.expected.y.wheelPageHeight);
+ is(Math.abs(aEvent.detail), expectedDetail,
+ description + ((isHorizontal) ? "horizontal" : "vertical") + " event detail is wrong");
+
+ if (--expectedHandlerCalls == 0) {
+ setTimeout(runTest, 0);
+ }
+ }
+
+ window.addEventListener("MozMousePixelScroll", handler, { capture: true, passive: false });
+
+ for (var i = 0; i < tests.length; i++) {
+ currentTest = tests[i];
+ description = "doTests, " + currentTest.description + " (deltaMode: " +
+ (currentTest.event.deltaMode == WheelEvent.DOM_DELTA_LINE ?
+ "DOM_DELTA_LINE" : "DOM_DELTA_PAGE") +
+ ", deltaX: " + currentTest.event.deltaX +
+ ", deltaY: " + currentTest.event.deltaY + "): ";
+ currentTest.prepare();
+ firedX = firedY = false;
+ expectedHandlerCalls = (currentTest.expected.x ? 1 : 0)
+ + (currentTest.expected.y ? 1 : 0);
+ yield synthesizeWheel(currentTest.target, 10, 10, currentTest.event);
+ if (currentTest.expected.x) {
+ ok(firedX, description + "Horizontal MozMousePixelScroll event wasn't fired");
+ }
+ if (currentTest.expected.y) {
+ ok(firedY, description + "Vertical MozMousePixelScroll event wasn't fired");
+ }
+ currentTest.cleanup();
+ }
+
+ window.removeEventListener("MozMousePixelScroll", handler, true);
+}
+
+function* testBody()
+{
+ yield* prepareScrollUnits();
+ yield* doTests();
+}
+
+var gTestContinuation = null;
+
+function runTest()
+{
+ if (!gTestContinuation) {
+ gTestContinuation = testBody();
+ }
+ var ret = gTestContinuation.next();
+ if (ret.done) {
+ SimpleTest.finish();
+ }
+}
+
+function startTest() {
+ SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.delta_multiplier_x", 100],
+ ["mousewheel.default.delta_multiplier_y", 100],
+ ["mousewheel.default.delta_multiplier_z", 100],
+ ["mousewheel.with_alt.delta_multiplier_x", 100],
+ ["mousewheel.with_alt.delta_multiplier_y", 100],
+ ["mousewheel.with_alt.delta_multiplier_z", 100],
+ ["mousewheel.with_control.delta_multiplier_x", 100],
+ ["mousewheel.with_control.delta_multiplier_y", 100],
+ ["mousewheel.with_control.delta_multiplier_z", 100],
+ ["mousewheel.with_meta.delta_multiplier_x", 100],
+ ["mousewheel.with_meta.delta_multiplier_y", 100],
+ ["mousewheel.with_meta.delta_multiplier_z", 100],
+ ["mousewheel.with_shift.delta_multiplier_x", 100],
+ ["mousewheel.with_shift.delta_multiplier_y", 100],
+ ["mousewheel.with_shift.delta_multiplier_z", 100],
+ // If APZ is enabled we should ensure the preventDefault calls work even
+ // if the test is running slowly.
+ ["apz.content_response_timeout", 2000],
+ ]}, runTest);
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_offsetxy.html b/dom/events/test/test_offsetxy.html
new file mode 100644
index 0000000000..693683d1b0
--- /dev/null
+++ b/dom/events/test/test_offsetxy.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for DOM MouseEvent offsetX/Y</title>
+ <script src="/tests/SimpleTest/SimpleTest.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="d" style="position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px"></div>
+<div id="d2" style="position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px; transform:translateX(100px)"></div>
+<div id="d3" style="display:none; position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px"></div>
+<div id="d4" style="transform:scale(0); position:absolute; top:100px; left:100px; width:100px; border:5px dotted black; height:100px"></div>
+
+<pre id="test">
+<script type="application/javascript">
+
+var offsetX = -1, offsetY = -1;
+var ev = new MouseEvent("click", {clientX:110, clientY:110});
+is(ev.offsetX, 110);
+is(ev.offsetY, 110);
+is(ev.offsetX, ev.pageX);
+is(ev.offsetY, ev.pageY);
+d.addEventListener("click", function (event) {
+ is(ev, event, "Event objects must match");
+ offsetX = event.offsetX;
+ offsetY = event.offsetY;
+});
+d.dispatchEvent(ev);
+is(offsetX, 5);
+is(offsetY, 5);
+is(ev.offsetX, 5);
+is(ev.offsetY, 5);
+
+var ev2 = new MouseEvent("click", {clientX:220, clientY:130});
+is(ev2.offsetX, 220);
+is(ev2.offsetY, 130);
+is(ev2.offsetX, ev2.pageX);
+is(ev2.offsetY, ev2.pageY);
+d2.addEventListener("click", function (event) {
+ is(ev2, event, "Event objects must match");
+ offsetX = event.offsetX;
+ offsetY = event.offsetY;
+});
+d2.dispatchEvent(ev2);
+is(offsetX, 15);
+is(offsetY, 25);
+is(ev2.offsetX, 15);
+is(ev2.offsetY, 25);
+
+var ev3 = new MouseEvent("click", {clientX:110, clientY:110});
+is(ev3.offsetX, 110);
+is(ev3.offsetY, 110);
+is(ev3.offsetX, ev3.pageX);
+is(ev3.offsetY, ev3.pageY);
+d3.addEventListener("click", function (event) {
+ is(ev3, event, "Event objects must match");
+ offsetX = event.offsetX;
+ offsetY = event.offsetY;
+});
+d3.dispatchEvent(ev3);
+is(offsetX, 0);
+is(offsetY, 0);
+is(ev3.offsetX, 0);
+is(ev3.offsetY, 0);
+
+var ev4 = new MouseEvent("click", {clientX:110, clientY:110});
+is(ev4.offsetX, 110);
+is(ev4.offsetY, 110);
+is(ev4.offsetX, ev4.pageX);
+is(ev4.offsetY, ev4.pageY);
+d4.addEventListener("click", function (event) {
+ is(ev4, event, "Event objects must match");
+ offsetX = event.offsetX;
+ offsetY = event.offsetY;
+});
+d4.dispatchEvent(ev4);
+is(offsetX, 0);
+is(offsetY, 0);
+is(ev4.offsetX, 0);
+is(ev4.offsetY, 0);
+
+// Now redispatch ev4 to "d" to make sure that its offsetX gets updated
+// relative to the new target. Have to set "ev" to "ev4", because the listener
+// on "d" expects to see "ev" as the event.
+ev = ev4;
+d.dispatchEvent(ev4);
+is(offsetX, 5);
+is(offsetY, 5);
+is(ev.offsetX, 5);
+is(ev.offsetY, 5);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_onerror_handler_args.html b/dom/events/test/test_onerror_handler_args.html
new file mode 100644
index 0000000000..cf925736d8
--- /dev/null
+++ b/dom/events/test/test_onerror_handler_args.html
@@ -0,0 +1,36 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1007790
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Test for Bug 1007790</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 1007790 **/
+ SimpleTest.waitForExplicitFinish();
+ addLoadEvent(function() {
+ is(frames[0].onerror.toString(),
+ "function onerror(event, source, lineno, colno, error) {\n\n}",
+ "Should have the right arguments for onerror on window");
+ is($("content").onerror.toString(),
+ "function onerror(event) {\n\n}",
+ "Should have the right arguments for onerror on element");
+ SimpleTest.finish();
+ });
+
+ </script>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1007790">Mozilla Bug 1007790</a>
+<p id="display"></p>
+<div id="content" style="display: none" onerror="">
+ <iframe srcdoc="<body onerror=''>"></iframe>
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_passive_listeners.html b/dom/events/test/test_passive_listeners.html
new file mode 100644
index 0000000000..dd132fc6bc
--- /dev/null
+++ b/dom/events/test/test_passive_listeners.html
@@ -0,0 +1,118 @@
+<html>
+<head>
+ <title>Tests for passive event listeners</title>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css">
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<body>
+<p id="display"></p>
+<div id="dummy">
+</div>
+
+<script>
+var listenerHitCount;
+var doPreventDefault;
+
+function listener(e)
+{
+ listenerHitCount++;
+ if (doPreventDefault) {
+ // When this function is registered as a passive listener, this
+ // call should be a no-op and might report a console warning.
+ e.preventDefault();
+ }
+}
+
+function listener2(e)
+{
+ if (doPreventDefault) {
+ e.preventDefault();
+ }
+}
+
+var elem = document.getElementById('dummy');
+
+function doTest(description, passiveArg)
+{
+ listenerHitCount = 0;
+
+ elem.addEventListener('test', listener, { passive: passiveArg });
+
+ // Test with a cancelable event
+ var e1 = new Event('test', { cancelable: true });
+ elem.dispatchEvent(e1);
+ is(listenerHitCount, 1, description + ' | hit count');
+ var expectedDefaultPrevented = (doPreventDefault && !passiveArg);
+ is(e1.defaultPrevented, expectedDefaultPrevented, description + ' | default prevented');
+
+ // Test with a non-cancelable event
+ var e2 = new Event('test', { cancelable: false });
+ elem.dispatchEvent(e2);
+ is(listenerHitCount, 2, description + ' | hit count after non-cancelable event');
+ is(e2.defaultPrevented, false, description + ' | default prevented on non-cancelable event');
+
+ // Test combining passive-enabled and "traditional" listeners
+ elem.addEventListener('test', listener2);
+ var e3 = new Event('test', { cancelable: true });
+ elem.dispatchEvent(e3);
+ is(listenerHitCount, 3, description + ' | hit count with second listener');
+ is(e3.defaultPrevented, doPreventDefault, description + ' | default prevented with second listener');
+ elem.removeEventListener('test', listener2);
+
+ elem.removeEventListener('test', listener);
+}
+
+function testAddListenerKey(passiveListenerFirst)
+{
+ listenerHitCount = 0;
+ doPreventDefault = true;
+
+ elem.addEventListener('test', listener, { capture: false, passive: passiveListenerFirst });
+ // This second listener should not be registered, because the "key" of
+ // { type, callback, capture } is the same, even though the 'passive' flag
+ // is different.
+ elem.addEventListener('test', listener, { capture: false, passive: !passiveListenerFirst });
+
+ var e1 = new Event('test', { cancelable: true });
+ elem.dispatchEvent(e1);
+
+ is(listenerHitCount, 1, 'Duplicate addEventListener was correctly ignored');
+ is(e1.defaultPrevented, !passiveListenerFirst, 'Prevent-default result based on first registered listener');
+
+ // Even though passive is the opposite of the first addEventListener call, it
+ // should remove the listener registered above.
+ elem.removeEventListener('test', listener, { capture: false, passive: !passiveListenerFirst });
+
+ var e2 = new Event('test', { cancelable: true });
+ elem.dispatchEvent(e2);
+
+ is(listenerHitCount, 1, 'non-passive listener was correctly unregistered');
+ is(e2.defaultPrevented, false, 'no listener was registered to preventDefault this event');
+}
+
+function test()
+{
+ doPreventDefault = false;
+
+ doTest('base case', undefined);
+ doTest('non-passive listener', false);
+ doTest('passive listener', true);
+
+ doPreventDefault = true;
+
+ doTest('base case', undefined);
+ doTest('non-passive listener', false);
+ doTest('passive listener', true);
+
+ testAddListenerKey(false);
+ testAddListenerKey(true);
+}
+
+test();
+
+</script>
+
+</body>
+</html>
+
+
diff --git a/dom/events/test/test_scroll_per_page.html b/dom/events/test/test_scroll_per_page.html
new file mode 100644
index 0000000000..8d464b1a43
--- /dev/null
+++ b/dom/events/test/test_scroll_per_page.html
@@ -0,0 +1,259 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Test for scroll per page</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>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(() => {
+ open("window_empty_document.html", "_blank", "width=500,height=500,scrollbars=yes");
+});
+
+async function doTests(aWindow) {
+ await SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false]]});
+ await SimpleTest.promiseFocus(aWindow);
+
+ function getNodeDescription(aNode) {
+ function getElementDescription(aElement) {
+ if (aElement.getAttribute("id") !== null) {
+ return `${aElement.tagName.toLowerCase()}#${aElement.getAttribute("id")}`;
+ }
+ if (aElement.tagName === "BR") {
+ return `${getElementDescription(aElement.previousSibling)} + br`;
+ }
+ return aElement.tagName.toLowerCase();
+ }
+ switch (aNode.nodeType) {
+ case aNode.TEXT_NODE:
+ return `text node in ${getElementDescription(aNode.parentElement)}`;
+ case aNode.ELEMENT_NODE:
+ return getElementDescription(aNode);
+ case aNode.DOCUMENT_NODE:
+ return `document node`;
+ default:
+ return "unknown node";
+ }
+ }
+
+ function getScrollPositionStr(aNode) {
+ return `{ scrollTop: ${aNode.scrollTop}, scrollHeight: ${
+ aNode.scrollHeight
+ }, scrollLeft: ${aNode.scrollLeft}, scrollWidth: ${aNode.scrollWidth} }`;
+ }
+
+ async function doPageDownOrUp(aKey, aFocusedElement, aScrollTargetElement) {
+ let scrollEventTarget =
+ aScrollTargetElement === doc.documentElement
+ ? doc
+ : aScrollTargetElement;
+ let scrollEventFired = false;
+ function onScroll(aEvent) {
+ scrollEventFired |= aEvent.target === scrollEventTarget;
+ }
+ scrollEventTarget.addEventListener("scroll", onScroll);
+ if (!navigator.platform.includes("Mac")) {
+ synthesizeKey(`KEY_${aKey}`, {}, aWindow);
+ } else {
+ synthesizeKey(`KEY_${aKey}`, { altKey: true }, aWindow);
+ }
+ let retry = 3;
+ while (retry--) {
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+ if (scrollEventFired) {
+ break;
+ }
+ }
+ ok(scrollEventFired,
+ `Scroll event should've been fired on ${getNodeDescription(scrollEventTarget)}`);
+ scrollEventTarget.removeEventListener("scroll", onScroll);
+ }
+
+ async function doPageDown(aFocusedElement, aScrollTargetElement) {
+ await doPageDownOrUp("PageDown", aFocusedElement, aScrollTargetElement);
+ }
+
+ async function doPageUp(aFocusedElement, aScrollTargetElement) {
+ await doPageDownOrUp("PageUp", aFocusedElement, aScrollTargetElement);
+ }
+
+ // Let's put log of scroll events for making debug this test easier.
+ aWindow.addEventListener("scroll", (aEvent) => {
+ let scrollElement =
+ aEvent.target === doc
+ ? doc.documentElement
+ : aEvent.target;
+ info(`"scroll" event fired on ${getNodeDescription(aEvent.target)}: ${
+ getScrollPositionStr(scrollElement)
+ }`);
+ }, { capture: true });
+
+ let doc = aWindow.document;
+ let body = doc.body;
+ let selection = doc.getSelection();
+ let container;
+
+ body.innerHTML = '<div id="largeDiv" style="height: 1500px;">' +
+ "<p>previous line of the editor.</p>" +
+ '<div id="editor" contenteditable style="margin-top 500px; height: 5em; overflow: auto;">' +
+ "Here is first line<br>" +
+ "Here is second line" +
+ "</div>" +
+ "<p>next line of the editor.</p>" +
+ "</div>";
+ container = doc.documentElement;
+ let editor = doc.getElementById("editor");
+ editor.focus();
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+
+ let description = "PageDown in non-scrollable editing host: ";
+ let previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description}the document should be scrolled down even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`);
+ let range = selection.getRangeAt(0);
+ is(range.startContainer, editor.firstChild.nextSibling.nextSibling,
+ `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`);
+ ok(range.collapsed, description + "selection should be collapsed");
+ is(doc.activeElement, editor,
+ description + "the editing host should keep having focus");
+
+ description = "PageUp in non-scrollable editing host: ";
+ previousScrollTop = container.scrollTop;
+ await doPageUp(editor, container);
+ ok(container.scrollTop < previousScrollTop,
+ `${description}the document should be scrolled up even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`);
+ range = selection.getRangeAt(0);
+ is(range.startContainer, editor.firstChild,
+ `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`);
+ ok(range.collapsed, description + "selection should be collapsed");
+ is(doc.activeElement, editor,
+ description + "the editing host should keep having focus");
+
+ body.innerHTML = '<div id="largeDiv" style="height: 1500px;">' +
+ "<p>previous line of the editor.</p>" +
+ '<div id="editor" contenteditable style="margin-top 500px; height: 5em; overflow: auto;">' +
+ '<div id="innerDiv" style="height: 10em;">' +
+ "Here is first line<br>" +
+ "Here is second line" +
+ "</div>" +
+ "</div>" +
+ "<p>next line of the editor.</p>" +
+ "</div>";
+ editor = doc.getElementById("editor");
+ container = editor;
+ editor.focus();
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+
+ description = "PageDown in scrollable editing host: ";
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description}the editor should be scrolled down even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`);
+ range = selection.getRangeAt(0);
+ is(range.startContainer, editor.firstChild.firstChild.nextSibling.nextSibling,
+ `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`);
+ ok(range.collapsed, description + "selection should be collapsed");
+ is(doc.activeElement, editor,
+ description + "the editing host should keep having focus");
+
+ description = "PageUp in scrollable editing host: ";
+ previousScrollTop = container.scrollTop;
+ await doPageUp(editor, container);
+ ok(container.scrollTop < previousScrollTop,
+ `${description}the editor should be scrolled up even if user presses PageDown in the editing host got: ${container.scrollTop}, previous position: ${previousScrollTop}`);
+ range = selection.getRangeAt(0);
+ is(range.startContainer, editor.firstChild.firstChild,
+ `${description}selection start shouldn't be moved to outside of the editing host (got: ${getNodeDescription(range.startContainer)})`);
+ ok(range.collapsed, description + "selection should be collapsed");
+ is(doc.activeElement, editor,
+ description + "the editing host should keep having focus");
+
+ // Should scroll one page of the scrollable element
+ body.innerHTML = `<div id="editor" contenteditable style="height: 1500px;">${"abc<br>".repeat(100)}</div>`;
+ editor = doc.getElementById("editor");
+ container = doc.documentElement;
+ editor.focus();
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+
+ description = "PageDown in too large editing host: ";
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} The document should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`);
+ ok(container.scrollTop <= previousScrollTop + container.clientHeight,
+ `${description} The document should not be scrolled down too much (got: ${container.scrollTop}, previous position: ${previousScrollTop}, scroll height: ${container.clientHeight})`);
+
+ selection.selectAllChildren(editor);
+ selection.collapseToEnd();
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+
+ description = "PageUp in too large editing host: ";
+ container.scrollTop = container.scrollHeight;
+ previousScrollTop = container.scrollTop;
+ await doPageUp(editor, container);
+ ok(container.scrollTop >= previousScrollTop - container.clientHeight,
+ `${description} The document should not be scrolled up too much (got: ${container.scrollTop}, previous position: ${previousScrollTop}, scroll height: ${container.clientHeight})`);
+
+ // Shouldn't scroll to caret position after pagedown scrolls editing host.
+ body.innerHTML = '<div id="editor" contenteditable style="height: 300px; overflow: auto;"><div style="height: 1500px;">abc<br>def<br></div></div>';
+ editor = doc.getElementById("editor");
+ container = editor;
+ editor.focus();
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+
+ description = "PageDown in scrollable editing host";
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} #1: Should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`);
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} #2: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`);
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} #3: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`);
+ await doPageUp(editor, container);
+ ok(container.scrollTop < 300,
+ `PageUp in scrollable editing host after scrolled down 3 pages: should be scrolled up to show caret (got:${container.scrollTop}`);
+
+ // Shouldn't scroll to caret position after pagedown scrolls outside of editing host.
+ // NOTE: We've set the window height is 500px above, but on Android, the viewport size depends on the screen size.
+ // Therefore, we need to compute enough height to test below with actual height of the window.
+ body.innerHTML = `<div id="editor" contenteditable style="height: ${aWindow.innerHeight * 3}px">abc<br>def<br></div>`;
+ editor = doc.getElementById("editor");
+ container = doc.documentElement;
+ editor.focus();
+ selection.collapse(editor.firstChild);
+ await waitToClearOutAnyPotentialScrolls(aWindow);
+
+ description = "PageDown in too high non-scrollable editing host";
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} #1: Should be scrolled down (got: ${container.scrollTop}, previous position: ${previousScrollTop})`);
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} #2: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`);
+ previousScrollTop = container.scrollTop;
+ await doPageDown(editor, container);
+ ok(container.scrollTop > previousScrollTop,
+ `${description} #3: should be scrolled down (got:${container.scrollTop}, previous position: ${previousScrollTop})`);
+ await doPageUp(editor, container);
+ ok(container.scrollTop < 300,
+ `PageUp in too high non-scrollable editing host after scrolled down 3 pages: should be scrolled up to show caret (got:${container.scrollTop}`);
+
+ aWindow.close();
+ SimpleTest.finish();
+}
+</script>
+</html>
diff --git a/dom/events/test/test_selection_after_right_click.html b/dom/events/test/test_selection_after_right_click.html
new file mode 100644
index 0000000000..3661db9c69
--- /dev/null
+++ b/dom/events/test/test_selection_after_right_click.html
@@ -0,0 +1,499 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8" />
+<title>Tests for checking the behavior of right click clicking outside selection</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<div>Non editable text</div>
+<div contenteditable>Editable text</div>
+<input value="input value" style="width: 100%" />
+<iframe srcdoc="<span>abc</span>"></iframe>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async () => {
+ function getRangeDescription(range) {
+ function getNodeDescription(node) {
+ if (!node) {
+ return "null";
+ }
+ switch (node.nodeType) {
+ case Node.TEXT_NODE:
+ case Node.COMMENT_NODE:
+ case Node.CDATA_SECTION_NODE:
+ return `${node.nodeName} "${node.data}"`;
+ case Node.ELEMENT_NODE:
+ return `<${node.nodeName.toLowerCase()}>`;
+ default:
+ return `${node.nodeName}`;
+ }
+ }
+ if (range === null) {
+ return "null";
+ }
+ if (range === undefined) {
+ return "undefined";
+ }
+ return range.startContainer == range.endContainer &&
+ range.startOffset == range.endOffset
+ ? `(${getNodeDescription(range.startContainer)}, ${range.startOffset})`
+ : `(${getNodeDescription(range.startContainer)}, ${
+ range.startOffset
+ }) - (${getNodeDescription(range.endContainer)}, ${range.endOffset})`;
+ }
+
+ document.addEventListener(
+ "contextmenu",
+ event => event.preventDefault(),
+ { capture: true }
+ );
+ const nonEditableDiv = document.querySelector("div");
+ const editableDiv = document.querySelector("div[contenteditable]");
+ const input = document.querySelector("input");
+
+ // See also modifying-selection-with-non-primary-mouse-button.tentative.html?secondary for the basic behavior check.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ],
+ });
+
+ getSelection().setBaseAndExtent(
+ nonEditableDiv.firstChild, "Non editable ".length,
+ nonEditableDiv.firstChild, "Non editable text".length
+ );
+ synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
+ ok(
+ getSelection().isCollapsed,
+ `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is false in non-editable text node (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ getSelection().setBaseAndExtent(
+ editableDiv.firstChild, "Editable ".length,
+ editableDiv.firstChild, "Editable text".length
+ );
+ synthesizeMouse(editableDiv, 10, 10, {button: 2});
+ ok(
+ getSelection().isCollapsed,
+ `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is false in editable text node (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ input.focus();
+ input.setSelectionRange("input ".length, "input value".length);
+ synthesizeMouse(input, 10, 10, {button: 2});
+ ok(
+ input.selectionStart == input.selectionEnd,
+ `Selection in <input> should be collapsed by a right click when stop_if_non_collapsed_selection pref is false (got: ${input.selectionStart} - ${input.selectionEnd})`
+ );
+ input.blur();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", true],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ],
+ });
+
+ getSelection().setBaseAndExtent(
+ nonEditableDiv.firstChild, "Non editable ".length,
+ nonEditableDiv.firstChild, "Non editable text".length
+ );
+ synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
+ ok(
+ !getSelection().isCollapsed,
+ `Selection should not be collapsed by a right click when stop_if_non_editable_node pref is true in non-editable text node (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ getSelection().setBaseAndExtent(
+ editableDiv.firstChild, "Editable ".length,
+ editableDiv.firstChild, "Editable text".length
+ );
+ synthesizeMouse(editableDiv, 10, 10, {button: 2});
+ ok(
+ getSelection().isCollapsed,
+ `Selection should be collapsed by a right click even when stop_if_non_editable_node pref is true in editable text node (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ input.focus();
+ input.setSelectionRange("input ".length, "input value".length);
+ synthesizeMouse(input, 10, 10, {button: 2});
+ ok(
+ input.selectionStart == input.selectionEnd,
+ `Selection in <input> should be collapsed by a right click even when stop_if_non_editable_node pref is true (got: ${input.selectionStart} - ${input.selectionEnd})`
+ );
+ input.blur();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ],
+ });
+
+ getSelection().setBaseAndExtent(
+ nonEditableDiv.firstChild, "Non editable ".length,
+ nonEditableDiv.firstChild, "Non editable text".length
+ );
+ synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
+ ok(
+ !getSelection().isCollapsed,
+ `Selection should not be collapsed by a right click when stop_if_non_collapsed_selection pref is true in non-editable text node (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ getSelection().setBaseAndExtent(
+ editableDiv.firstChild, "Editable ".length,
+ editableDiv.firstChild, "Editable text".length
+ );
+ synthesizeMouse(editableDiv, 10, 10, {button: 2});
+ ok(
+ !getSelection().isCollapsed,
+ `Selection should be collapsed by a right click when stop_if_non_collapsed_selection pref is true in editable text node (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ input.focus();
+ input.setSelectionRange("input ".length, "input value".length);
+ synthesizeMouse(input, 10, 10, {button: 2});
+ ok(
+ input.selectionStart != input.selectionEnd,
+ `Selection in <input> should not be collapsed by a right click when stop_if_non_collapsed_selection pref is true (got: ${input.selectionStart} - ${input.selectionEnd})`
+ );
+ input.blur();
+
+ // When the right click changes the selection ancestor limit, it should be
+ // handled correctly after nsIFrame does nothing.
+ getSelection().setBaseAndExtent(
+ nonEditableDiv.firstChild, "Non editable ".length,
+ nonEditableDiv.firstChild, "Non editable text".length
+ );
+ synthesizeMouse(editableDiv, 10, 10, {button: 2});
+ is(
+ document.activeElement,
+ editableDiv,
+ "Right clicking in editable <div> when selection selects some text outside the <div> should move focus into the editor"
+ );
+ ok(
+ getSelection().focusNode == editableDiv.firstChild &&
+ getSelection().focusOffset > 0,
+ `Right clicking in editable <div> when selection selects some text outside the <div> should not cause collapsing selection to start of the editor (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ getSelection().setBaseAndExtent(
+ editableDiv.firstChild, "Editable ".length,
+ editableDiv.firstChild, "Editable text".length
+ );
+ synthesizeMouse(nonEditableDiv, 10, 10, {button: 2});
+ isnot(
+ document.activeElement,
+ editableDiv,
+ "Right clicking outside the editable <div> should blur from it"
+ );
+ ok(
+ getSelection().focusNode == nonEditableDiv.firstChild &&
+ getSelection().focusOffset > 0,
+ `Right clicking outside the editable <div> should collapse selection at the clicked content (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ // If clicking in a selection range, the range should never be collapsed.
+ const iframe = document.querySelector("iframe");
+ const doc = iframe.contentDocument;
+ doc.addEventListener("contextmenu", event => event.preventDefault(), {capture: true});
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", false],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ],
+ });
+ iframe.focus();
+ doc.getSelection().selectAllChildren(doc.body);
+ synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow);
+ ok(
+ !doc.getSelection().isCollapsed,
+ "Right click in a selection range should not cause collapsing selection"
+ );
+ iframe.blur();
+ doc.activeElement?.blur();
+
+ doc.documentElement.setAttribute("contenteditable", "");
+ iframe.focus();
+ doc.getSelection().selectAllChildren(doc.body);
+ synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow);
+ ok(
+ !doc.getSelection().isCollapsed,
+ "Right click in a selection range in a editable-root-element should not cause collapsing selection"
+ );
+ iframe.blur();
+ doc.activeElement?.blur();
+
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ],
+ });
+ doc.designMode = "on";
+ iframe.focus();
+ doc.getSelection().selectAllChildren(doc.body);
+ synthesizeMouseAtCenter(doc.body.firstChild, {button: 2}, iframe.contentWindow);
+ ok(
+ !doc.getSelection().isCollapsed,
+ "Right click in a selection range in a sub-editable-document should not cause collapsing selection"
+ );
+ doc.designMode = "off";
+
+ // If selection is collapsed and `ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node` is
+ // false, the caret should be moved at clicked point.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ],
+ });
+ editableDiv.innerHTML = "<b>Bold text</b><i>Italic text</i>";
+ editableDiv.focus();
+ getSelection().collapse(
+ editableDiv.querySelector("b").firstChild,
+ "Bold".length
+ );
+ synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2});
+ ok(
+ getSelection().isCollapsed,
+ "Right clicking in editable <div> should keep Selection collapsed if the pref is unset"
+ );
+ is(
+ getSelection().focusNode.parentNode,
+ editableDiv.querySelector("i"),
+ `Right clicking in editable <div> should collapse Selection at the clicked point (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ }) if the pref is unset`
+ );
+
+ input.focus();
+ input.setSelectionRange(0, 0);
+ synthesizeMouseAtCenter(input, {button: 2});
+ ok(
+ input.selectionStart == input.selectionEnd,
+ "Right click in <input> should keep Selection collapsed if the pref is unset"
+ );
+ ok(
+ input.selectionStart > 0,
+ "Right click in <input> should move caret if the pref is unset"
+ );
+ input.blur();
+
+ // If selection is collapsed and `ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node` is
+ // true, the caret should not be moved at clicked point unless focus is changed by the click.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", true],
+ ],
+ });
+ editableDiv.innerHTML = "<b>Bold text</b><i>Italic text</i>";
+ editableDiv.focus();
+ getSelection().collapse(
+ editableDiv.querySelector("b").firstChild,
+ "Bold".length
+ );
+ synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2});
+ ok(
+ getSelection().isCollapsed,
+ "Right clicking in editable <div> should keep Selection collapsed even if the pref is set"
+ );
+ is(
+ getSelection().focusNode.parentNode,
+ editableDiv.querySelector("b"),
+ `Right clicking in editable <div> should not move caret to the clicked point (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ }) if the pref is set`
+ );
+
+ input.focus();
+ input.setSelectionRange(0, 0);
+ synthesizeMouseAtCenter(input, {button: 2});
+ ok(
+ input.selectionStart == input.selectionEnd,
+ "Right click in <input> should keep Selection collapsed even if the pref is set"
+ );
+ is(
+ input.selectionStart,
+ 0,
+ "Right click in <input> should not move caret if the pref is set"
+ );
+ input.blur();
+
+ input.setSelectionRange(0, 0);
+ editableDiv.focus();
+ synthesizeMouseAtCenter(input, {button: 2});
+ ok(
+ input.selectionStart == input.selectionEnd,
+ "Right click in <input> which is not focused should keep Selection collapsed"
+ );
+ ok(
+ input.selectionStart > 0,
+ "Right click in <input> which is not focused should move caret"
+ );
+
+ editableDiv.focus();
+ getSelection().collapse(
+ editableDiv.querySelector("b").firstChild,
+ "Bold".length
+ );
+ input.focus();
+ synthesizeMouseAtCenter(editableDiv.querySelector("i"), {button: 2});
+ ok(
+ getSelection().isCollapsed,
+ "Right clicking in editable <div> which is not focused should collapse selection"
+ );
+ is(
+ getSelection().focusNode.parentNode,
+ editableDiv.querySelector("i"),
+ `Right clicking in editable <div> which is not focused should collapse Selection at the clicked point (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ // If Shift + right click should forcibly open context menu, users may want the click to work as
+ // same as without Shift.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ["dom.event.contextmenu.shift_suppresses_event", true],
+ ],
+ });
+ nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
+ getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0);
+ synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2});
+ ok(
+ getSelection().isCollapsed,
+ `Selection should be collapsed by a Shift + right click on non-editable node when it does not open context menu (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+ is(
+ getSelection().focusNode,
+ nonEditableDiv.querySelector("i").firstChild,
+ `Selection should be collapsed at the click point by a Shift + right click on non-editable node when it does not open context menu (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ editableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
+ getSelection().collapse(editableDiv.querySelector("b").firstChild, 0);
+ synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2});
+ ok(
+ getSelection().isCollapsed,
+ `Selection should be collapsed by a Shift + right click on editable node when it does not open context menu (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+ is(
+ getSelection().focusNode,
+ editableDiv.querySelector("i").firstChild,
+ `Selection should be collapsed at the click point by a Shift + right click on editable node when it does not open context menu (${
+ getRangeDescription(getSelection().getRangeAt(0))
+ })`
+ );
+
+ input.focus();
+ input.setSelectionRange(0, 0);
+ synthesizeMouseAtCenter(input, {shiftKey: true, button: 2});
+ ok(
+ input.selectionStart == input.selectionEnd,
+ `Selection in <input> should be collapsed by a Shift + right click when it does not open context menu (got: ${
+ input.selectionStart
+ } - ${input.selectionEnd})`
+ );
+ isnot(
+ input.selectionStart,
+ 0,
+ `Selection in <input> should be collapsed at the click point by a Shift + right click when it does not open context menu (got: ${
+ input.selectionStart
+ } - ${input.selectionEnd})`
+ );
+ input.blur();
+
+ // If Shift + right click should open context menu, users may want the click to work as
+ // same as a left click.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_collapsed_selection", true],
+ ["ui.mouse.right_click.collapse_selection.stop_if_non_editable_node", false],
+ ["ui.mouse.right_click.move_caret.stop_if_in_focused_editable_node", false],
+ ["dom.event.contextmenu.shift_suppresses_event", false],
+ ],
+ });
+ nonEditableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
+ getSelection().collapse(nonEditableDiv.querySelector("b").firstChild, 0);
+ synthesizeMouseAtCenter(nonEditableDiv.querySelector("i"), {shiftKey: true, button: 2});
+ is(
+ getRangeDescription(getSelection().getRangeAt(0)),
+ getRangeDescription({
+ startContainer: nonEditableDiv.querySelector("b").firstChild,
+ startOffset: 0,
+ endContainer: nonEditableDiv.querySelector("i").firstChild,
+ endOffset: getSelection().focusOffset,
+ }),
+ `Selection should be extended by a Shift + right click on non-editable node when it should open context menu`
+ );
+
+ editableDiv.innerHTML = "<b>bold</b> <i>italic</i>";
+ getSelection().collapse(editableDiv.querySelector("b").firstChild, 0);
+ synthesizeMouseAtCenter(editableDiv.querySelector("i"), {shiftKey: true, button: 2});
+ is(
+ getRangeDescription(getSelection().getRangeAt(0)),
+ getRangeDescription({
+ startContainer: editableDiv.querySelector("b").firstChild,
+ startOffset: 0,
+ endContainer: editableDiv.querySelector("i").firstChild,
+ endOffset: getSelection().focusOffset,
+ }),
+ `Selection should be extended by a Shift + right click on editable node when it should open context menu`
+ );
+
+ input.focus();
+ input.setSelectionRange(0, 0);
+ synthesizeMouseAtCenter(input, {shiftKey: true, button: 2});
+ isnot(
+ input.selectionStart,
+ input.selectionEnd,
+ `Selection in <input> should be extended by a Shift + right click when it should open context menu (got: ${
+ input.selectionStart
+ } - ${input.selectionEnd})`
+ );
+ input.blur();
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_slotted_mouse_event.html b/dom/events/test/test_slotted_mouse_event.html
new file mode 100644
index 0000000000..e8a0cdc9f4
--- /dev/null
+++ b/dom/events/test/test_slotted_mouse_event.html
@@ -0,0 +1,102 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Bug 1481500: mouse enter / leave events in slotted content</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script>
+// We move the mouse from the #host to #target, then to #child-target.
+//
+// By the time we get to #child-target, we shouldn't have fired any mouseleave.
+function runTests() {
+ let iframe = document.createElement('iframe');
+ iframe.style.width = "600px";
+ iframe.style.height = "600px";
+ document.body.appendChild(iframe);
+ iframe.onload = () => frameLoaded(iframe);
+ iframe.srcdoc = `
+ <style>
+ body {
+ padding: 20px;
+ }
+ #child-target {
+ width: 80px;
+ height: 80px;
+ background: yellow;
+ }
+ </style>
+ <div id="host"><div id="target"><div id="child-target"></div></div></div>
+ `;
+}
+
+function frameLoaded(iframe) {
+ let host = iframe.contentDocument.getElementById('host');
+ let target = iframe.contentDocument.getElementById('target');
+ let childTarget = iframe.contentDocument.getElementById('child-target');
+ let sawHost = false;
+ let sawTarget = false;
+ let finished = false;
+
+ host.attachShadow({ mode: 'open' }).innerHTML = `
+ <style>
+ :host {
+ width: 500px;
+ height: 500px;
+ background: purple;
+ }
+ ::slotted(div) {
+ width: 200px;
+ height: 200px;
+ background: green;
+ }
+ </style>
+ <slot></slot>
+ `;
+
+ synthesizeMouse(document.body, 10, 10, { type: "mousemove" });
+
+ host.addEventListener("mouseenter", e => {
+ if (finished)
+ return;
+ sawHost = true;
+ ok(true, "Should fire mouseenter on the host.");
+ });
+
+ host.addEventListener("mouseleave", e => {
+ if (finished)
+ return;
+ ok(false, "Should not fire mouseleave when moving the cursor to the slotted target");
+ });
+
+ target.addEventListener("mouseenter", () => {
+ if (finished)
+ return;
+ ok(sawHost, "Should've seen the hostmouseenter already");
+ sawTarget = true;
+ ok(true, "Moving the mouse into the target should trigger a mouseenter there");
+ });
+
+ target.addEventListener("mouseleave", () => {
+ if (finished)
+ return;
+ ok(false, "Should not fire mouseleave when moving the cursor to the slotted target's child");
+ });
+
+ childTarget.addEventListener("mouseenter", () => {
+ if (finished)
+ return;
+ ok(sawTarget, "Should've seen the target mouseenter already");
+ finished = true;
+ SimpleTest.finish();
+ });
+
+ synthesizeMouseAtCenter(host, { type: "mousemove" });
+ synthesizeMouseAtCenter(target, { type: "mousemove" });
+ synthesizeMouseAtCenter(childTarget, { type: "mousemove" });
+}
+
+SimpleTest.waitForExplicitFinish();
+window.onload = () => {
+ SimpleTest.waitForFocus(runTests);
+};
+</script>
diff --git a/dom/events/test/test_slotted_text_click.html b/dom/events/test/test_slotted_text_click.html
new file mode 100644
index 0000000000..34464bd918
--- /dev/null
+++ b/dom/events/test/test_slotted_text_click.html
@@ -0,0 +1,72 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Bug 1481500: click / activation on text activates the slot it's assigned to</title>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<link rel="stylesheet" href="/tests/SimpleTest/test.css">
+<script>
+function generateLotsOfText() {
+ let text = "Some text. ";
+ for (let i = 0; i < 10; ++i)
+ text += text;
+ return text;
+}
+
+function runTests() {
+ let iframe = document.createElement('iframe');
+ document.body.appendChild(iframe);
+ iframe.onload = () => frameLoaded(iframe);
+ iframe.width = "350"
+ iframe.height = "350"
+ iframe.srcdoc =
+ `<div id="host">${generateLotsOfText()}</div>`
+}
+
+function frameLoaded(iframe) {
+ let host = iframe.contentDocument.getElementById('host');
+
+ host.attachShadow({ mode: 'open' }).innerHTML = `
+ <style>
+ :host {
+ width: 300px;
+ height: 300px;
+ overflow: hidden;
+ }
+ </style>
+ <slot></slot>
+ `;
+
+ let slot = host.shadowRoot.querySelector('slot');
+ let mousedownFired = false;
+ let mouseupFired = false;
+ slot.addEventListener('mousedown', function() {
+ ok(true, "Mousedown should fire on the slot when clicking on text");
+ mousedownFired = true;
+ });
+
+ slot.addEventListener('click', function() {
+ ok(true, "Click should target the slot");
+ ok(mousedownFired, "mousedown should've fired");
+ ok(mouseupFired, "click should've fired");
+ SimpleTest.finish();
+ });
+
+ slot.addEventListener('mouseup', function() {
+ // FIXME: When we fix bug 1481517, this check should move to the mousedown listener.
+ ok(this.matches(":active"), "Slot should become active");
+ mouseupFired = true;
+ });
+
+ requestAnimationFrame(() => {
+ requestAnimationFrame(() => {
+ synthesizeMouseAtPoint(150, 150, { type: "mousedown" });
+ synthesizeMouseAtPoint(150, 150, { type: "mouseup" });
+ });
+ });
+}
+
+SimpleTest.waitForExplicitFinish();
+window.onload = () => {
+ SimpleTest.waitForFocus(runTests);
+};
+</script>
diff --git a/dom/events/test/test_submitevent_on_form.html b/dom/events/test/test_submitevent_on_form.html
new file mode 100644
index 0000000000..01cd8f28ee
--- /dev/null
+++ b/dom/events/test/test_submitevent_on_form.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test submit event on form</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+ <form action="javascript:doDefault()" id="form">
+ <input name="n1" value="v1">
+ <input type="submit" value="Do Default Action">
+ </form>
+ <pre id="test">
+ <script type="application/javascript">
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests);
+
+var defaultActionCount = 0;
+
+function doDefault()
+{
+ defaultActionCount++;
+}
+
+async function runTests()
+{
+ const form = document.getElementById("form");
+ let formDataInEvent = null;
+
+ // Test if submission from submit() is deferred.
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.forms.submit.trusted_event_only", false]],
+ });
+
+ form.addEventListener("submit", function(e) {
+ form.elements[0].value = "v1";
+ form.submit();
+ form.elements[0].value = "v2";
+ }, { once: true });
+
+ form.addEventListener('formdata', e => {
+ formDataInEvent = e.formData;
+ });
+
+ form.dispatchEvent(new Event('submit'));
+ await new Promise(resolve => SimpleTest.executeSoon(resolve));
+
+ is(defaultActionCount, 1, "should only submit once");
+ is(formDataInEvent.get('n1'), 'v2', "submission from submit() should be deferred");
+
+ // Test untrusted submit event shouldn't trigger form default action.
+ defaultActionCount = 0;
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.forms.submit.trusted_event_only", true]],
+ });
+
+ form.dispatchEvent(new Event('submit'));
+ setTimeout(() => {
+ is(defaultActionCount, 0, "untrusted submit event shouldn't trigger form default action");
+ SimpleTest.finish();
+ });
+}
+ </script>
+ </pre>
+</body>
+</html>
diff --git a/dom/events/test/test_text_event_in_content.html b/dom/events/test/test_text_event_in_content.html
new file mode 100644
index 0000000000..3d7735edb1
--- /dev/null
+++ b/dom/events/test/test_text_event_in_content.html
@@ -0,0 +1,68 @@
+<!doctype html>
+<html>
+<head>
+ <title>Not dispatching DOM "text" event on web apps</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<input id="input">
+<textarea id="textarea"></textarea>
+<div contenteditable id="editor"><p><br></p></div>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function doTests() {
+ for (let editorId of ["input", "textarea", "editor"]) {
+ let editor = document.getElementById(editorId);
+ editor.focus();
+ let fired = false;
+ function onText() {
+ fired = true;
+ }
+ editor.addEventListener("text", onText);
+
+ fired = false;
+ synthesizeCompositionChange({
+ composition: {string: "abc",
+ clauses: [{length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
+ caret: {start: 3, length: 0},
+ });
+ ok(!fired, `Starting composition shouldn't fire DOM "text" event in ${editorId}`);
+ fired = false;
+ synthesizeComposition({type: "compositioncommitasis", key: {key: "KEY_Enter"}});
+ ok(!fired, `Committing composition with the latest string shouldn't fire DOM "text" event in ${editorId}`);
+
+ fired = false;
+ synthesizeCompositionChange({
+ composition: {string: "def",
+ clauses: [{length: 3, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
+ caret: {start: 3, length: 0},
+ });
+ ok(!fired, `Restarting composition shouldn't fire DOM "text" event in ${editorId}`);
+ fired = false;
+ synthesizeComposition({type: "compositioncommit", data: "", key: {key: "KEY_Escape"}});
+ ok(!fired, `Committing composition with empty string shouldn't fire DOM "text" event in ${editorId}`);
+
+ fired = false;
+ synthesizeCompositionChange({
+ composition: {string: "de",
+ clauses: [{length: 2, attr: COMPOSITION_ATTR_RAW_CLAUSE}]},
+ caret: {start: 2, length: 0},
+ });
+ ok(!fired, `Restarting composition shouldn't fire DOM "text" event in ${editorId}`);
+ fired = false;
+ synthesizeComposition({type: "compositioncommit", data: "def", key: {key: "KEY_Escape"}});
+ ok(!fired, `Committing composition with new string shouldn't fire DOM "text" event in ${editorId}`);
+
+ fired = false;
+ synthesizeComposition({type: "compositioncommit", data: "ghi"});
+ ok(!fired, `Inserting string shouldn't fire DOM "text" event in ${editorId}`);
+
+ editor.removeEventListener("text", onText);
+ }
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_unbound_before_in_active_chain.html b/dom/events/test/test_unbound_before_in_active_chain.html
new file mode 100644
index 0000000000..b62d44bb31
--- /dev/null
+++ b/dom/events/test/test_unbound_before_in_active_chain.html
@@ -0,0 +1,38 @@
+<!doctype html>
+<title>Test for bug 1489139: Unbound generated content in the active chain</title>
+<script src="/tests/SimpleTest/EventUtils.js"></script>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+<style>
+#target, #target::before {
+ width: 200px;
+ height: 200px;
+}
+
+#target::before {
+ content: " ";
+ display: block;
+ background: green;
+}
+
+#target:active::before {
+ content: "";
+ background: red;
+}
+</style>
+Should see a green square after clicking.
+<div id="target"></div>
+<script>
+SimpleTest.waitForExplicitFinish();
+onload = function() {
+ let target = document.getElementById("target");
+ requestAnimationFrame(() => {
+ synthesizeMouseAtPoint(100, 100, { type: "mousedown" })
+ ok(target.matches(":active"), "Should have been clicked");
+ requestAnimationFrame(() => {
+ synthesizeMouseAtPoint(100, 100, { type: "mouseup" })
+ ok(!target.matches(':active'), "Should stop matching :active afterwards");
+ SimpleTest.finish();
+ });
+ });
+}
+</script>
diff --git a/dom/events/test/test_use_conflated_keypress_event_model_on_newer_Office_Online_Server.html b/dom/events/test/test_use_conflated_keypress_event_model_on_newer_Office_Online_Server.html
new file mode 100644
index 0000000000..2e897a48c0
--- /dev/null
+++ b/dom/events/test/test_use_conflated_keypress_event_model_on_newer_Office_Online_Server.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1545410
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testing whether "keypress" event model is forcibly conflated model if the document is newer Office Online Server instance</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=1545410">Bug 1545410</a>
+<p id="display"></p>
+<pre id="test"></pre>
+<input id="input">
+<iframe id="iframe" srcdoc='<html><body><div id="WACViewPanel_EditingElement" spellcheck="false" class="FireFox usehover WACEditing EditMode EditingSurfaceBody WACViewPanel_DisableLegacyKeyCodeAndCharCode" style="overflow: visible; visibility: visible;" contenteditable="true"></div></body></html>'></iframe>
+<script>
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function doTests() {
+ let iframe = document.getElementById("iframe");
+ iframe.contentDocument.body.firstChild.focus();
+ let keypressEvent;
+ iframe.contentDocument.body.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("a", {}, iframe.contentWindow);
+ is(keypressEvent.keyCode, "a".charCodeAt(0),
+ "keyCode value of 'a' should be 'a'");
+ is(keypressEvent.charCode, "a".charCodeAt(0),
+ "charCode value of 'a' should be 'a'");
+
+ iframe.contentDocument.body.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("KEY_Enter", {}, iframe.contentWindow);
+ is(keypressEvent.keyCode, KeyboardEvent.DOM_VK_RETURN,
+ "keyCode value of 'Enter' should be DOM_VK_RETURN");
+ is(keypressEvent.charCode, KeyboardEvent.DOM_VK_RETURN,
+ "charCode value of 'Enter' should be DOM_VK_RETURN");
+
+ let input = document.getElementById("input");
+ input.focus();
+ input.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("a", {});
+ is(keypressEvent.keyCode, "a".charCodeAt(0),
+ "keyCode value of 'a' in the parent document should be 'a'");
+ is(keypressEvent.charCode, "a".charCodeAt(0),
+ "charCode value of 'a' in the parent document should be 'a'");
+
+ input.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("KEY_Enter");
+ is(keypressEvent.keyCode, KeyboardEvent.DOM_VK_RETURN,
+ "keyCode value of 'Enter' in the parent document should be DOM_VK_RETURN");
+ is(keypressEvent.charCode, KeyboardEvent.DOM_VK_RETURN,
+ "charCode value of 'Enter' in the parent document should be DOM_VK_RETURN");
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html b/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html
new file mode 100644
index 0000000000..3898584b0a
--- /dev/null
+++ b/dom/events/test/test_use_split_keypress_event_model_on_old_Confluence.html
@@ -0,0 +1,82 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1514940
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testing whether "keypress" event model is forcibly split model if the document is old Confluence instance</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=1514940">Bug 1514940</a>
+<p id="display"></p>
+<pre id="test"></pre>
+<input id="input">
+<iframe id="iframe" srcdoc="<html><body><p>Here is editor</p></body></html>"></iframe>
+<script>
+// Emulate window.tinyMCE.CursorTargetPlugin().getInfo() which is referred by
+// KeyPresEventModelCheckerChild.
+class CursorTargetPluginImpl {
+ getInfo() {
+ return {
+ longname: "Cursor Target plugin",
+ author: "Atlassian",
+ authorurl: "http://www.atlassian.com",
+ version: "1.0",
+ };
+ }
+}
+var tinyMCE = {
+ plugins: {
+ CursorTargetPlugin: CursorTargetPluginImpl,
+ },
+};
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(async function doTests() {
+ let iframe = document.getElementById("iframe");
+ let waitForCheckKeyPressEventModelEvent = new Promise(resolve => {
+ SpecialPowers.addSystemEventListener(iframe.contentDocument, "CheckKeyPressEventModel", resolve, {once: true});
+ });
+ iframe.contentDocument.body.setAttribute("contenteditable", "true");
+ await waitForCheckKeyPressEventModelEvent;
+ iframe.contentDocument.body.focus();
+ let keypressEvent;
+ iframe.contentDocument.body.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("a", {}, iframe.contentWindow);
+ is(keypressEvent.keyCode, 0,
+ "keyCode value of 'a' should be 0");
+ is(keypressEvent.charCode, "a".charCodeAt(0),
+ "charCode value of 'a' should be 'a'");
+
+ iframe.contentDocument.body.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("KEY_Enter", {}, iframe.contentWindow);
+ is(keypressEvent.keyCode, KeyboardEvent.DOM_VK_RETURN,
+ "keyCode value of 'Enter' should be DOM_VK_RETURN");
+ is(keypressEvent.charCode, 0,
+ "charCode value of 'Enter' should be 0");
+
+ let input = document.getElementById("input");
+ input.focus();
+ input.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("a", {});
+ is(keypressEvent.keyCode, "a".charCodeAt(0),
+ "keyCode value of 'a' in the parent document should be 'a'");
+ is(keypressEvent.charCode, "a".charCodeAt(0),
+ "charCode value of 'a' in the parent document should be 'a'");
+
+ input.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("KEY_Enter");
+ is(keypressEvent.keyCode, KeyboardEvent.DOM_VK_RETURN,
+ "keyCode value of 'Enter' in the parent document should be DOM_VK_RETURN");
+ is(keypressEvent.charCode, KeyboardEvent.DOM_VK_RETURN,
+ "charCode value of 'Enter' in the parent document should be DOM_VK_RETURN");
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html b/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html
new file mode 100644
index 0000000000..68f52be41d
--- /dev/null
+++ b/dom/events/test/test_use_split_keypress_event_model_on_old_Office_Online_Server.html
@@ -0,0 +1,69 @@
+<!DOCTYPE html>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1545410
+-->
+<head>
+ <meta charset="utf-8">
+ <title>Testing whether "keypress" event model is forcibly split model if the document is old Office Online Server instance</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=1545410">Bug 1545410</a>
+<p id="display"></p>
+<pre id="test"></pre>
+<input id="input">
+<iframe id="iframe" onload="srcdocLoaded()" srcdoc='<html><body><div id="WACViewPanel_EditingElement" spellcheck="false" class="FireFox usehover WACEditing EditMode EditingSurfaceBody" style="overflow: visible; visibility: visible;"></div></body></html>'></iframe>
+<script>
+SimpleTest.waitForExplicitFinish();
+let waitForCheckKeyPressEventModelEvent;
+function srcdocLoaded() {
+ waitForCheckKeyPressEventModelEvent = new Promise(resolve => {
+ dump(document.querySelector("iframe").contentDocument.location + "\n");
+ var doc = document.querySelector("iframe").contentDocument;
+ SpecialPowers.addSystemEventListener(doc, "CheckKeyPressEventModel", resolve, {once: true});
+ doc.getElementById("WACViewPanel_EditingElement").contentEditable = "true";
+ });
+}
+SimpleTest.waitForFocus(async function doTests() {
+ let iframe = document.getElementById("iframe");
+ await waitForCheckKeyPressEventModelEvent;
+ iframe.contentDocument.body.firstChild.focus();
+ let keypressEvent;
+ iframe.contentDocument.body.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("a", {}, iframe.contentWindow);
+ is(keypressEvent.keyCode, 0,
+ "keyCode value of 'a' should be 0");
+ is(keypressEvent.charCode, "a".charCodeAt(0),
+ "charCode value of 'a' should be 'a'");
+
+ iframe.contentDocument.body.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("KEY_Enter", {}, iframe.contentWindow);
+ is(keypressEvent.keyCode, KeyboardEvent.DOM_VK_RETURN,
+ "keyCode value of 'Enter' should be DOM_VK_RETURN");
+ is(keypressEvent.charCode, 0,
+ "charCode value of 'Enter' should be 0");
+
+ let input = document.getElementById("input");
+ input.focus();
+ input.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("a", {});
+ is(keypressEvent.keyCode, "a".charCodeAt(0),
+ "keyCode value of 'a' in the parent document should be 'a'");
+ is(keypressEvent.charCode, "a".charCodeAt(0),
+ "charCode value of 'a' in the parent document should be 'a'");
+
+ input.addEventListener("keypress", aEvent => keypressEvent = aEvent, {once: true});
+ synthesizeKey("KEY_Enter");
+ is(keypressEvent.keyCode, KeyboardEvent.DOM_VK_RETURN,
+ "keyCode value of 'Enter' in the parent document should be DOM_VK_RETURN");
+ is(keypressEvent.charCode, KeyboardEvent.DOM_VK_RETURN,
+ "charCode value of 'Enter' in the parent document should be DOM_VK_RETURN");
+
+ SimpleTest.finish();
+});
+</script>
+</body>
+</html>
diff --git a/dom/events/test/test_wheel_default_action.html b/dom/events/test/test_wheel_default_action.html
new file mode 100644
index 0000000000..ba5d956d4b
--- /dev/null
+++ b/dom/events/test/test_wheel_default_action.html
@@ -0,0 +1,39 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for default action of WheelEvent</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>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+SpecialPowers.pushPrefEnv({"set": [
+ ["mousewheel.system_scroll_override.enabled", false]
+]}, runTest);
+
+var subWin = null;
+
+function runTest() {
+ subWin = window.open("window_wheel_default_action.html", "_blank",
+ "width=500,height=500,scrollbars=yes");
+}
+
+function finish()
+{
+ subWin.close();
+ SimpleTest.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/test_wheel_zoom_on_form_controls.html b/dom/events/test/test_wheel_zoom_on_form_controls.html
new file mode 100644
index 0000000000..73fc13558c
--- /dev/null
+++ b/dom/events/test/test_wheel_zoom_on_form_controls.html
@@ -0,0 +1,69 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Zoom using wheel should work on form controls</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <link rel="stylesheet" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<button id="button" style="width:10px;height:10px;"></button><br>
+<input id="input" style="border: 10px solid green;"><br>
+<textarea id="textarea" style="border: 10px solid green;"></textarea><br>
+<select id="select"><option></option></select>
+<select id="list" size=4>
+ <option>XXXXXXXXXX</option>
+ <option>XXXXXXXXXX</option>
+ <option>XXXXXXXXXX</option>
+ <option>XXXXXXXXXX</option>
+ <option>XXXXXXXXXX</option>
+ <option>XXXXXXXXXX</option>
+</select>
+<script>
+
+ async function testControl(id) {
+ var initialZoom = SpecialPowers.getFullZoom(window);
+ var element = document.getElementById(id);
+
+ const zoomHasHappened = SimpleTest.promiseWaitForCondition(() => {
+ const zoom = SpecialPowers.getFullZoom(window);
+ return (zoom != initialZoom);
+ }, id + ": wheel event changed the zoom.");
+
+ let event = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaY: 3,
+ ctrlKey: true
+ };
+ synthesizeWheel(element, 5, 5, event);
+
+ await zoomHasHappened;
+ isnot(SpecialPowers.getFullZoom(window), initialZoom, id + ": should have zoomed");
+ SpecialPowers.setFullZoom(window, initialZoom);
+ }
+
+ async function test() {
+ await testControl("button");
+ await testControl("input");
+ await testControl("textarea");
+ await testControl("select");
+ await testControl("list");
+ SimpleTest.finish();
+ }
+
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(() => {
+ SpecialPowers.pushPrefEnv({
+ "set":[
+ ["mousewheel.with_control.action", 3], // Scroll on Ctrl + mousewheel
+ ["test.events.async.enabled", true],
+ ]
+ }, test)
+ });
+</script>
+</body>
+</html>
diff --git a/dom/events/test/window_bug1369072.html b/dom/events/test/window_bug1369072.html
new file mode 100644
index 0000000000..94b147d38f
--- /dev/null
+++ b/dom/events/test/window_bug1369072.html
@@ -0,0 +1,170 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1369072
+-->
+<head>
+ <title>Test for Bug 1369072</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <script 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>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1369072">Mozilla Bug 1369072</a>
+<div id="display">
+<iframe id="iframe" srcdoc="<a id='anchor' href='about:home'>anchor text</a><div id='div'></div>" style="width: 300px; height: 300px;"></iframe>
+<!-- make <body> contents overflow -->
+<div style="width: 1000px; height: 1000px;"></div>
+</div>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(runTests, window);
+
+function ok()
+{
+ window.opener.ok.apply(window.opener, arguments);
+}
+
+function is()
+{
+ window.opener.is.apply(window.opener, arguments);
+}
+
+async function runTests()
+{
+ var iframe = document.getElementById("iframe");
+ var anchor = iframe.contentDocument.getElementById("anchor");
+ var div = iframe.contentDocument.getElementById("div");
+
+ async function resetScroll()
+ {
+ var oldFocus = document.activeElement;
+ var oldFrameFocus = iframe.contentDocument.activeElement;
+
+ // Cancel any scroll animation on the target scroll elements to make sure
+ // setting scrollTop or scrolLeft works as expected.
+ // cancelScrollAnimation clears focus, so make sure to restore it.
+ await cancelScrollAnimation(document.documentElement);
+ await cancelScrollAnimation(iframe.contentDocument.documentElement);
+
+ oldFocus.focus();
+ oldFrameFocus.focus();
+
+ return new Promise(resolve => {
+ var scrollParent = document.documentElement.scrollTop || document.documentElement.scrollLeft;
+ var scrollChild = iframe.contentDocument.documentElement.scrollTop || iframe.contentDocument.documentElement.scrollLeft;
+ if (scrollParent) {
+ window.addEventListener("scroll", () => {
+ scrollParent = false;
+ if (!scrollChild) {
+ SimpleTest.executeSoon(resolve);
+ }
+ }, { once: true });
+ }
+ if (scrollChild) {
+ iframe.contentWindow.addEventListener("scroll", () => {
+ scrollChild = false;
+ if (!scrollParent) {
+ SimpleTest.executeSoon(resolve);
+ }
+ }, { once: true });
+ }
+ document.documentElement.scrollTop = 0;
+ document.documentElement.scrollLeft = 0;
+ iframe.contentDocument.documentElement.scrollTop = 0;
+ iframe.contentDocument.documentElement.scrollLeft = 0;
+ if (!scrollParent && !scrollChild) {
+ SimpleTest.executeSoon(resolve);
+ }
+ });
+ }
+
+ async function tryToScrollWithKey(aVertical)
+ {
+ await resetScroll();
+
+ return new Promise(resolve => {
+ // Wait scroll event
+ function onScroll() {
+ SimpleTest.executeSoon(resolve);
+ }
+ window.addEventListener("scroll", onScroll, { once: true });
+ iframe.contentWindow.addEventListener("scroll", onScroll, { once: true });
+
+ if (aVertical) {
+ synthesizeKey("KEY_ArrowDown");
+ } else {
+ synthesizeKey("KEY_ArrowRight");
+ }
+ });
+ }
+
+ // When iframe element has focus and the iframe document isn't scrollable, the parent document should be scrolled instead.
+ document.body.focus();
+ iframe.focus();
+ await tryToScrollWithKey(true);
+ ok(document.documentElement.scrollTop > 0, "ArrowDown keydown event at the iframe whose content is not scrollable should cause scrolling the parent document");
+ await tryToScrollWithKey(false);
+ ok(document.documentElement.scrollLeft > 0, "ArrowRight keydown event at the iframe whose content is not scrollable should cause scrolling the parent document");
+ await resetScroll();
+
+ // When iframe element has focus and the iframe document scrollable, the parent document shouldn't be scrolled.
+ document.body.focus();
+ div.style.height = "1000px";
+ div.style.width = "1000px";
+ iframe.focus();
+ await tryToScrollWithKey(true);
+ is(document.documentElement.scrollTop, 0, "ArrowDown keydown event at the iframe whose content is scrollable shouldn't cause scrolling the parent document");
+ ok(iframe.contentDocument.documentElement.scrollTop > 0, "ArrowDown keydown event at the iframe whose content is scrollable should cause scrolling the iframe document");
+ await tryToScrollWithKey(false);
+ is(document.documentElement.scrollLeft, 0, "ArrowRight keydown event at the iframe whose content is scrollable shouldn't cause scrolling the parent document");
+ ok(iframe.contentDocument.documentElement.scrollLeft > 0, "ArrowRight keydown event at the iframe whose content is scrollable should cause scrolling the iframe document");
+ await resetScroll();
+
+ // If iframe document cannot scroll to specific direction, parent document should be scrolled instead.
+ div.style.height = "1px";
+ div.style.width = "1000px";
+ iframe.focus();
+ await tryToScrollWithKey(true);
+ ok(document.documentElement.scrollTop > 0, "ArrowDown keydown event at the iframe whose content is scrollable only horizontally should cause scrolling the parent document");
+ await tryToScrollWithKey(false);
+ is(document.documentElement.scrollLeft, 0, "ArrowRight keydown event at the iframe whose content is scrollable only horizontally shouldn't cause scrolling the parent document");
+ ok(iframe.contentDocument.documentElement.scrollLeft > 0, "ArrowRight keydown event at the iframe whose content is scrollable only horizontally should cause scrolling the iframe document");
+ await resetScroll();
+
+ div.style.height = "1000px";
+ div.style.width = "1px";
+ iframe.focus();
+ await tryToScrollWithKey(true);
+ is(document.documentElement.scrollTop, 0, "ArrowDown keydown event at the iframe whose content is scrollable only vertically shouldn't cause scrolling the parent document");
+ ok(iframe.contentDocument.documentElement.scrollTop > 0, "ArrowDown keydown event at the iframe whose content is scrollable only vertically should cause scrolling the iframe document");
+ await tryToScrollWithKey(false);
+ ok(document.documentElement.scrollLeft > 0, "ArrowRight keydown event at the iframe whose content is scrollable only vertically should cause scrolling the parent document");
+ await resetScroll();
+
+ // Hidden iframe shouldn't consume keyboard events if it was not scrollable.
+ document.body.focus();
+ anchor.focus();
+ iframe.style.display = "none";
+ await tryToScrollWithKey(true);
+ ok(document.documentElement.scrollTop > 0, "ArrowDown keydown event after hiding the iframe should cause scrolling the parent document");
+ await tryToScrollWithKey(false);
+ ok(document.documentElement.scrollLeft > 0, "ArrowRight keydown event after hiding the iframe should cause scrolling the parent document");
+ await resetScroll();
+
+ // Make sure the result visible in the viewport.
+ document.documentElement.scrollTop = 0;
+ document.documentElement.scrollLeft = 0;
+ window.opener.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/events/test/window_bug1412775.xhtml b/dom/events/test/window_bug1412775.xhtml
new file mode 100644
index 0000000000..e1274c7bb5
--- /dev/null
+++ b/dom/events/test/window_bug1412775.xhtml
@@ -0,0 +1,8 @@
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="640" height="480">
+
+ <browser id="browser" type="content" primary="true" flex="1" src="about:blank"/>
+
+</window>
diff --git a/dom/events/test/window_bug1429572.html b/dom/events/test/window_bug1429572.html
new file mode 100644
index 0000000000..694794b1e4
--- /dev/null
+++ b/dom/events/test/window_bug1429572.html
@@ -0,0 +1,351 @@
+<html>
+ <head>
+ <title></title>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+ var tests = [
+ simpleNonShadowTest,
+ nonShadowInputDate,
+ jsDispatchedNonShadowTouchEvents,
+ shadowDOMTest1,
+ shadowDOMTest2,
+ shadowDOMTest3,
+ jsDispatchedShadowTouchEvents,
+ jsDispatchedShadowTouchEvents2
+ ];
+ var host;
+ var host2;
+ var shadowRoot;
+ var shadowRoot2;
+ var winUtils;
+
+ var touchCounter = 0;
+ function createTouchArray(targetList) {
+ var touchArray = [];
+ for (var i = 0; i < targetList.length; ++i) {
+ touchArray.push(new Touch({identifier: ++touchCounter, target: targetList[i]}));
+ }
+ return touchArray;
+ }
+
+ function synthesizeTouches(targets, xOffsets) {
+ if (xOffsets) {
+ opener.is(targets.length, xOffsets.length, "Wrong xOffsets length!");
+ }
+ var touches = [];
+ var xs = [];
+ var ys = [];
+ var rxs = [];
+ var rys = [];
+ var angles = [];
+ var forces = [];
+ var tiltXs = [];
+ var tiltYs = [];
+ var twists = [];
+ for (var i = 0; i < targets.length; ++i) {
+ touches.push(++touchCounter);
+ var rect = targets[i].getBoundingClientRect();
+ if (xOffsets) {
+ xs.push(rect.left + (rect.width / 2) + xOffsets[i]);
+ } else {
+ xs.push(rect.left + (rect.width / 2));
+ }
+ ys.push(rect.top + (rect.height / 2));
+ rxs.push(1);
+ rys.push(1);
+ angles.push(0);
+ forces.push(1);
+ tiltXs.push(0);
+ tiltYs.push(0);
+ twists.push(0);
+ }
+ winUtils.sendTouchEvent("touchstart",
+ touches, xs, ys, rxs, rys, angles, forces, tiltXs, tiltYs, twists, 0);
+ winUtils.sendTouchEvent("touchend",
+ touches, xs, ys, rxs, rys, angles, forces, tiltXs, tiltYs, twists, 0);
+ }
+
+ function next() {
+ if (!tests.length) {
+ opener.done();
+ window.close();
+ } else {
+ var test = tests.shift();
+ requestAnimationFrame(function() { setTimeout(test); });
+ }
+ }
+
+ function simpleNonShadowTest() {
+ var s1 = document.getElementById("span1");
+ var s2 = document.getElementById("span2");
+ var s3 = document.getElementById("span3");
+ var nonShadow = document.getElementById("nonshadow");
+ var event;
+ nonShadow.ontouchstart = function(e) {
+ event = e;
+ opener.is(e.targetTouches.length, 1, "Should have only one entry in targetTouches.");
+ opener.is(e.targetTouches[0].target, e.target, "targetTouches should contain event.target.");
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+ synthesizeTouches([s1, s2, s3]);
+
+ opener.is(event.targetTouches.length, 1, "Should have only one entry in targetTouches. (2)");
+ opener.is(event.targetTouches[0].target, event.target, "targetTouches should contain event.target. (2)");
+ opener.is(event.touches.length, 3, "touches list should contain all the touch objects. (2)");
+ opener.is(event.changedTouches.length, 3, "changedTouches list should contain all the touch objects. (2)");
+
+ next();
+ }
+
+ function jsDispatchedNonShadowTouchEvents() {
+ var s1 = document.getElementById("span1");
+ var s2 = document.getElementById("span2");
+ var s3 = document.getElementById("span3");
+ var nonShadow = document.getElementById("nonshadow");
+ var didCallListener = false;
+ nonShadow.ontouchstart = function(e) {
+ didCallListener = true;
+ opener.is(e.targetTouches.length, 3, "Should have all the entries in targetTouches.");
+ opener.is(e.targetTouches[0].target, s1, "targetTouches should contain s1 element.");
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+ var touchArray = createTouchArray([s1, s2, s3]);
+ var touchEvent = new TouchEvent("touchstart",
+ {
+ touches: touchArray,
+ targetTouches: touchArray,
+ changedTouches: touchArray
+ });
+ opener.is(touchEvent.targetTouches.length, 3, "Should have 3 entries in targetTouches");
+ nonShadow.dispatchEvent(touchEvent);
+ opener.ok(didCallListener, "Should have called the listener.");
+ opener.is(touchEvent.targetTouches.length, 3, "Should have all the entries in targetTouches. (2)");
+ opener.is(touchEvent.targetTouches[0].target, s1, "targetTouches should contain s1 element. (2)");
+ opener.is(touchEvent.touches.length, 3, "touches list should contain all the touch objects. (2)");
+ opener.is(touchEvent.changedTouches.length, 3, "changedTouches list should contain all the touch objects. (2)");
+
+ nonShadow.ontouchstart = null;
+ next();
+ }
+
+ function nonShadowInputDate() {
+ // This is a test for dispathing several touches to an element with
+ // native anonymous content.
+ var s1 = document.getElementById("span1");
+ var date = document.getElementById("date");
+ var nonShadow = document.getElementById("nonshadow");
+ var hasDateAsTarget = false;
+ var didCallListener = false;
+ nonShadow.ontouchstart = function(e) {
+ didCallListener = true;
+ if (e.targetTouches[0].target == date) {
+ hasDateAsTarget = true;
+ opener.is(e.targetTouches.length, 2, "Should have two entries in targetTouches.");
+ opener.is(e.targetTouches[0].target, e.target, "targetTouches should contain date.");
+ opener.is(e.targetTouches[1].target, e.target, "targetTouches should contain date twice.");
+ }
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+
+ var rect = date.getBoundingClientRect();
+ var quarter = rect.width / 4;
+ synthesizeTouches([date, date, s1], [-quarter, quarter, 0]);
+ opener.ok(didCallListener, "Should have called listener.");
+ opener.ok(hasDateAsTarget, "Should have seen touchstart with date element as the target.")
+ nonShadow.ontouchstart = null;
+ next();
+ }
+
+ function shadowDOMTest1() {
+ var shadowS1 = shadowRoot.getElementById("shadowSpan1");
+
+ // Ensure retargeting works.
+ var hostHandled = false;
+ host.ontouchstart = function(e) {
+ hostHandled = true;
+ opener.is(e.targetTouches.length, 1, "Should have only one entry in targetTouches.");
+ opener.is(e.targetTouches[0].target, e.target, "targetTouches should contain event.target.");
+ opener.is(e.target, host, "Event and touch should have been retargeted.");
+ opener.is(e.touches.length, 1, "touches list should contain one touch object.");
+ opener.is(e.changedTouches.length, 1, "changedTouches list should contain one touch objects.");
+ }
+
+ // Ensure retargeting doesn't happen inside shadow DOM.
+ var shadowHandled = false;
+ shadowS1.ontouchstart = function(e) {
+ shadowHandled = true;
+ opener.is(e.targetTouches.length, 1, "Should have only one entry in targetTouches.");
+ opener.is(e.targetTouches[0].target, e.target, "targetTouches should contain event.target.");
+ opener.is(e.target, shadowS1, "Event and touch should not have been retargeted.");
+ opener.is(e.touches.length, 1, "touches list should contain one touch object.");
+ opener.is(e.changedTouches.length, 1, "changedTouches list should contain one touch objects.");
+ }
+ synthesizeTouches([shadowS1]);
+ opener.ok(hostHandled, "Should have called listener on host.");
+ opener.ok(shadowHandled, "Should have called listener on shadow DOM element.");
+ host.ontouchstart = null;
+ shadowS1.ontouchstart = null;
+
+ next();
+ }
+
+ function shadowDOMTest2() {
+ var shadowS1 = shadowRoot.getElementById("shadowSpan1");
+ var shadowS2 = shadowRoot.getElementById("shadowSpan2");
+ var s1 = document.getElementById("span1");
+
+ var hostHandled = false;
+ host.ontouchstart = function(e) {
+ opener.is(e.target, host, "Event.target should be the host element.");
+ hostHandled = true;
+ opener.is(e.targetTouches.length, 2, "Should have two entries in targetTouches.");
+ opener.is(e.targetTouches[0].target, e.target, "targetTouches should contain event.target.");
+ opener.is(e.targetTouches[1].target, e.target, "targetTouches should contain event.target twice.");
+ opener.is(e.touches.length, 3, "touches list should contain one touch object.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain one touch objects.");
+ }
+
+ synthesizeTouches([shadowS1, shadowS2, s1]);
+ opener.ok(hostHandled, "Should have called listener on host.");
+ host.ontouchstart = null;
+
+ next();
+ }
+
+
+ function shadowDOMTest3() {
+ var shadowS1 = shadowRoot.getElementById("shadowSpan1");
+ var shadowS2 = shadowRoot2.getElementById("shadowSpan2");
+ var s1 = document.getElementById("span1");
+
+ var hostHandled = false;
+ host.ontouchstart = function(e) {
+ opener.is(e.target, host, "Event.target should be the host element.");
+ hostHandled = true;
+ opener.is(e.targetTouches.length, 1, "Should have one entry in targetTouches.");
+ opener.is(e.targetTouches[0].target, e.target, "targetTouches should contain event.target.");
+ opener.is(e.touches.length, 3, "touches list should contain one touch object.");
+ opener.is(e.touches[0].target, host, "Should have retargeted the first Touch object.");
+ opener.is(e.touches[1].target, host2, "Should have retargeted the second Touch object.");
+ opener.is(e.touches[3].target, s1, "Touch object targeted to light DOM should keep its target as is.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain one touch objects.");
+ }
+
+ synthesizeTouches([shadowS1, shadowS2, s1]);
+ opener.ok(hostHandled, "Should have called listener on host.");
+ host.ontouchstart = null;
+
+ next();
+ }
+
+ function jsDispatchedShadowTouchEvents() {
+ var s1 = document.getElementById("span1");
+ var shadowS1 = shadowRoot.getElementById("shadowSpan1");
+ var shadowS2 = shadowRoot.getElementById("shadowSpan2");
+ var hostHandled = false;
+ var shadowHandled = false;
+ host.ontouchstart = function(e) {
+ hostHandled = true;
+ opener.is(e.targetTouches.length, 2, "Should have all the shadow entries in targetTouches.");
+ opener.is(e.targetTouches[0].target, host, "targetTouches shouldn't reveal shadow DOM.");
+ opener.is(e.targetTouches[1].target, host, "targetTouches shouldn't reveal shadow DOM.");
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+ shadowS1.ontouchstart = function(e) {
+ shadowHandled = true;
+ opener.is(e.targetTouches.length, 3, "Should have all the in targetTouches.");
+ opener.is(e.targetTouches[0].target, shadowS1, "targetTouches should contain two shadow elements.");
+ opener.is(e.targetTouches[1].target, shadowS2, "targetTouches should contain two shadow elements.");
+ opener.is(e.targetTouches[2].target, s1, "targetTouches should contain a slight element.");
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+ var touchArray = createTouchArray([shadowS1, shadowS2, s1]);
+ var touchEvent = new TouchEvent("touchstart",
+ {
+ composed: true,
+ touches: touchArray,
+ targetTouches: touchArray,
+ changedTouches: touchArray
+ });
+ opener.is(touchEvent.targetTouches.length, 3, "Should have 3 entries in targetTouches");
+ shadowS1.dispatchEvent(touchEvent);
+ opener.ok(hostHandled, "Should have called listener on host.");
+ opener.ok(shadowHandled, "Should have called listener on shadow DOM element.");
+ host.ontouchstart = null;
+ shadowS1.ontouchstart = null;
+ next();
+ }
+
+ function jsDispatchedShadowTouchEvents2() {
+ var s1 = document.getElementById("span1");
+ var shadowS1 = shadowRoot.getElementById("shadowSpan1");
+ var shadowS2 = shadowRoot2.getElementById("shadowSpan2");
+ var hostHandled = false;
+ var shadowHandled = false;
+ host.ontouchstart = function(e) {
+ hostHandled = true;
+ opener.is(e.targetTouches.length, 1, "Should have one shadow entry in targetTouches.");
+ opener.is(e.targetTouches[0].target, host, "targetTouches shouldn't reveal shadow DOM.");
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.touches[0].target, host, "Should have retargeted the first Touch object.");
+ opener.is(e.touches[1].target, host2, "Should have retargeted the second Touch object.");
+ opener.is(e.touches[2].target, s1, "Touch object targeted to light DOM should keep its target as is.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+ shadowS1.ontouchstart = function(e) {
+ shadowHandled = true;
+ opener.is(e.targetTouches.length, 3, "Should have all the in targetTouches.");
+ opener.is(e.targetTouches[0].target, shadowS1, "targetTouches should contain two shadow elements.");
+ opener.is(e.targetTouches[1].target, shadowS2, "targetTouches should contain two shadow elements.");
+ opener.is(e.targetTouches[2].target, s1, "targetTouches should contain a slight element.");
+ opener.is(e.touches.length, 3, "touches list should contain all the touch objects.");
+ opener.is(e.changedTouches.length, 3, "changedTouches list should contain all the touch objects.");
+ }
+ var touchArray = createTouchArray([shadowS1, shadowS2, s1]);
+ var touchEvent = new TouchEvent("touchstart",
+ {
+ composed: true,
+ touches: touchArray,
+ targetTouches: touchArray,
+ changedTouches: touchArray
+ });
+ opener.is(touchEvent.targetTouches.length, 3, "Should have 3 entries in targetTouches");
+ shadowS1.dispatchEvent(touchEvent);
+ opener.ok(hostHandled, "Should have called listener on host.");
+ opener.ok(shadowHandled, "Should have called listener on shadow DOM element.");
+ host.ontouchstart = null;
+ shadowS1.ontouchstart = null;
+ next();
+ }
+
+ function start() {
+ winUtils = _getDOMWindowUtils(this);
+ host = document.getElementById("host");
+ shadowRoot = host.attachShadow({ mode: "open" });
+ shadowRoot.innerHTML =
+ "<span id='shadowSpan1'>shadowFoo </span><span id='shadowSpan2'>shadowBar </span><span id='shadowSpan3'>shadowBaz </span><slot></slot>";
+
+ host2 = document.getElementById("host2");
+ shadowRoot2 = host2.attachShadow({ mode: "open" });
+ shadowRoot2.innerHTML =
+ "<span id='shadowSpan1'>shadowFoo </span><span id='shadowSpan2'>shadowBar </span><span id='shadowSpan3'>shadowBaz </span><slot></slot>";
+ next();
+ }
+ </script>
+ <style>
+ </style>
+ </head>
+ <body onload="start();">
+ <div id="nonshadow">
+ <span id="span1">foo </span><span id="span2">bar </span><span id="span3"> baz</span>
+ <input type="date" id="date">
+ </div>
+ <div id="host"><span id="assignedNode"> assigned node </span></div>
+ <div id="host2"><span id="assignedNode2"> assigned node 2 </span></div>
+ </body>
+</html>
diff --git a/dom/events/test/window_bug1447993.html b/dom/events/test/window_bug1447993.html
new file mode 100644
index 0000000000..64bedf658c
--- /dev/null
+++ b/dom/events/test/window_bug1447993.html
@@ -0,0 +1,239 @@
+<!DOCTYPE HTML>
+<html>
+ <head>
+ <title>Test for Bug 1447993</title>
+ <style>
+ #area {
+ background: green;
+ border: 1px solid black;
+ width: 40px;
+ height: 40px;
+ }
+
+ #target {
+ background: blue;
+ border: 1px solid black;
+ width: 20px;
+ height: 20px;
+ margin: 10px;
+ }
+ </style>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script>
+
+ var tests = [
+ topLevelDocumentEventHandling,
+ topLevelDocumentEventHandlingWithTouch,
+ iframeEventHandling,
+ ];
+
+ function next() {
+ if (!tests.length) {
+ opener.done();
+ window.close();
+ } else {
+ var test = tests.shift();
+ requestAnimationFrame(function() { setTimeout(test); });
+ }
+ }
+
+ function start() {
+ next();
+ }
+
+ function topLevelDocumentEventHandling() {
+ var pid;
+ var area = document.getElementById("area");
+ var target = document.getElementById("target");
+ var body = document.body;
+ var html = document.documentElement;
+ var eventLog = [];
+ function captureEvent(e) {
+ eventLog.push([e.type, e.composedPath()]);
+ }
+ var expectedEvents = [
+ ["pointerdown", [ target, area, body, html, document, window ]],
+ ["mousedown", [ target, area, body, html, document, window ]],
+ ["pointerup", [ area, body, html, document, window ]],
+ ["mouseup", [ area, body, html, document, window ]],
+ ["click", [ target, area, body, html, document, window ]],
+ ];
+
+ window.addEventListener("pointerdown",
+ function(e) {
+ captureEvent(e);
+ pid = e.pointerId;
+ area.setPointerCapture(pid);
+ }, { once: true});
+ window.addEventListener("mousedown",
+ function(e) {
+ captureEvent(e);
+ }, { once: true});
+ window.addEventListener("pointerup",
+ function(e) {
+ captureEvent(e);
+ area.releasePointerCapture(pid);
+ }, { once: true});
+ window.addEventListener("mouseup", function(e) {
+ captureEvent(e);
+ }, { once: true});
+ window.addEventListener("click", function(e) {
+ captureEvent(e);
+ }, { once: true});
+
+ synthesizeMouseAtCenter(target, {}, window);
+
+ opener.is(eventLog.length, expectedEvents.length,
+ "[topLevelDocumentEventHandling] Same number events expected.");
+ for (var i = 0; i < eventLog.length; ++i) {
+ opener.is(eventLog[i][0], expectedEvents[i][0],
+ `topLevelDocumentEventHandling ${i}`);
+ for (var j = 0; j < eventLog[i][1].length; ++j) {
+ opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
+ `topLevelDocumentEventHandling ${i} ${j}`);
+ }
+ }
+ next();
+ }
+
+ function topLevelDocumentEventHandlingWithTouch() {
+ var pid;
+ var area = document.getElementById("area");
+ var target = document.getElementById("target");
+ var body = document.body;
+ var html = document.documentElement;
+ var eventLog = [];
+ function captureEvent(e) {
+ eventLog.push([e.type, e.composedPath()]);
+ }
+ var expectedEvents = [
+ ["pointerdown", [ target, area, body, html, document, window ]],
+ ["touchstart", [ target, area, body, html, document, window ]],
+ ["pointerup", [ area, body, html, document, window ]],
+ ["touchend", [ target, area, body, html, document, window ]],
+ /*
+ // Right now touch event initated mousedown/up (and then click) are
+ // dispatched in APZ, and there isn't JS exposed way to test that.
+ ["mousedown", [ target, area, body, html, document, window ]],
+ ["mouseup", [ target, area, body, html, document, window ]],
+ ["click", [ target, area, body, html, document, window ]],*/
+ ];
+
+ window.addEventListener("pointerdown",
+ function(e) {
+ captureEvent(e);
+ pid = e.pointerId;
+ area.setPointerCapture(pid);
+ }, { once: true});
+ window.addEventListener("touchstart", function(e) {
+ captureEvent(e);
+ }, { once: true});
+ window.addEventListener("pointerup",
+ function(e) {
+ captureEvent(e);
+ try {
+ area.releasePointerCapture(pid);
+ } catch(ex) {}
+ }, { once: true});
+ window.addEventListener("touchend", function(e) {
+ captureEvent(e);
+ }, { once: true});
+ /*window.addEventListener("mousedown",
+ function(e) {
+ captureEvent(e);
+ }, { once: true});
+ window.addEventListener("mouseup", function(e) {
+ captureEvent(e);
+ }, { once: true});
+ window.addEventListener("click", function(e) {
+ captureEvent(e);
+ }, { once: true});*/
+
+ synthesizeTouchAtCenter(target, {}, window);
+
+ opener.is(eventLog.length, expectedEvents.length,
+ "[topLevelDocumentEventHandlingWithTouch] Same number events expected.");
+ for (var i = 0; i < eventLog.length; ++i) {
+ opener.is(eventLog[i][0], expectedEvents[i][0],
+ `topLevelDocumentEventHandlingWithTouch ${i}`);
+ for (var j = 0; j < eventLog[i][1].length; ++j) {
+ opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
+ `topLevelDocumentEventHandlingWithTouch ${i} ${j}`);
+ }
+ }
+ next();
+ }
+
+ function iframeEventHandling() {
+ var pid;
+ var iframe = document.getElementById("iframe");
+ var doc = iframe.contentDocument;
+ doc.head.innerHTML = "<style>" + document.getElementsByTagName("style")[0].textContent + "</style>";
+ var area = doc.createElement("div");
+ area.id = "area";
+ var target = doc.createElement("div");
+ target.id = "target";
+ area.appendChild(target);
+ doc.body.appendChild(area);
+ var body = doc.body;
+ var html = doc.documentElement;
+ var win = doc.defaultView;
+ var eventLog = [];
+ function captureEvent(e) {
+ eventLog.push([e.type, e.composedPath()]);
+ }
+ var expectedEvents = [
+ ["pointerdown", [ target, area, body, html, doc, win ]],
+ ["mousedown", [ target, area, body, html, doc, win ]],
+ ["pointerup", [ area, body, html, doc, win ]],
+ ["mouseup", [ area, body, html, doc, win ]],
+ ["click", [ target, area, body, html, doc, win ]],
+ ];
+
+ win.addEventListener("pointerdown",
+ function(e) {
+ captureEvent(e);
+ pid = e.pointerId;
+ area.setPointerCapture(pid);
+ }, { once: true});
+ win.addEventListener("mousedown",
+ function(e) {
+ captureEvent(e);
+ }, { once: true});
+ win.addEventListener("pointerup",
+ function(e) {
+ captureEvent(e);
+ area.releasePointerCapture(pid);
+ }, { once: true});
+ win.addEventListener("mouseup", function(e) {
+ captureEvent(e);
+ }, { once: true});
+ win.addEventListener("click", function(e) {
+ captureEvent(e);
+ }, { once: true});
+
+ synthesizeMouseAtCenter(target, {}, win);
+
+ opener.is(eventLog.length, expectedEvents.length,
+ "[iframeEventHandling] Same number events expected.");
+ for (var i = 0; i < eventLog.length; ++i) {
+ opener.is(eventLog[i][0], expectedEvents[i][0],
+ `iframeEventHandling ${i}`);
+ for (var j = 0; j < eventLog[i][1].length; ++j) {
+ opener.is(eventLog[i][1][j], expectedEvents[i][1][j],
+ `iframeEventHandling ${i} ${j}`);
+ }
+ }
+ next();
+ }
+
+ </script>
+ </head>
+ <body onload="start();">
+ <div id="area">
+ <div id="target"></div>
+ </div>
+ <iframe id="iframe"></iframe>
+ <h5 id="targetOutsideIframe"></h5>
+ </body>
+</html>
diff --git a/dom/events/test/window_bug493251.html b/dom/events/test/window_bug493251.html
new file mode 100644
index 0000000000..d2891a9ef2
--- /dev/null
+++ b/dom/events/test/window_bug493251.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.waitForFocus(window.opener.doTest, window);
+ </script>
+</head>
+<body>
+ <input>
+</body>
+</html>
diff --git a/dom/events/test/window_bug617528.xhtml b/dom/events/test/window_bug617528.xhtml
new file mode 100644
index 0000000000..fce6f510b2
--- /dev/null
+++ b/dom/events/test/window_bug617528.xhtml
@@ -0,0 +1,9 @@
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ width="640" height="480">
+
+ <browser id="browser" type="content" primary="true" flex="1" src="about:blank"
+ disablehistory="true"/>
+
+</window>
diff --git a/dom/events/test/window_bug659071.html b/dom/events/test/window_bug659071.html
new file mode 100644
index 0000000000..7f0c8b676c
--- /dev/null
+++ b/dom/events/test/window_bug659071.html
@@ -0,0 +1,85 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for Bug 659071</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>
+<video id="v" controls></video>
+<script type="application/javascript">
+
+SimpleTest.waitForFocus(runTests, window);
+
+function ok() {
+ window.opener.ok.apply(window.opener, arguments);
+}
+
+function info() {
+ window.opener.info.apply(window.opener, arguments);
+}
+
+async function waitForCondition(aFunc) {
+ for (let i = 0; i < 30; i++) {
+ await (new Promise(resolve => requestAnimationFrame(() => requestAnimationFrame(resolve))));
+ if (aFunc(SpecialPowers.getFullZoom(window))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+async function runTests() {
+ await SpecialPowers.pushPrefEnv(
+ {
+ "set": [
+ ["mousewheel.with_control.action", 3],
+ ["test.events.async.enabled", true],
+ ]
+ }
+ );
+
+ const video = document.getElementById("v");
+
+ for (let i = 0; i < 3; i++) {
+ synthesizeWheel(video, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE, ctrlKey: true,
+ deltaX: 0, deltaY: 1.0, lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 });
+ if (await waitForCondition(aZoomLevel => aZoomLevel < 1.0)) {
+ break;
+ }
+ info("Retrying zoom out...");
+ }
+ let zoomLevel = SpecialPowers.getFullZoom(window);
+ ok(
+ zoomLevel < 1.0,
+ `Failed to zoom out with turning wheel to bottom, got: ${zoomLevel}`
+ );
+
+ for (let i = 0; i < 4; i++) {
+ synthesizeWheel(video, 10, 10,
+ { deltaMode: WheelEvent.DOM_DELTA_LINE, ctrlKey: true,
+ deltaX: 0, deltaY: -1.0, lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 });
+ if (await waitForCondition(aZoomLevel => aZoomLevel > 1.0)) {
+ break;
+ }
+ info("Retrying zoom in...");
+ }
+ zoomLevel = SpecialPowers.getFullZoom(window);
+ ok(
+ zoomLevel > 1.0,
+ `Failed to zoom in with turning wheel to top, got: ${zoomLevel}`
+ );
+
+ synthesizeKey("0", { accelKey: true });
+ ok(
+ await waitForCondition(aZoomLevel => aZoomLevel == 1.0),
+ "Failed to reset zoom at finish the test"
+ );
+ opener.finish();
+}
+
+</script>
+</body>
+</html>
diff --git a/dom/events/test/window_empty_document.html b/dom/events/test/window_empty_document.html
new file mode 100644
index 0000000000..bc90e91eab
--- /dev/null
+++ b/dom/events/test/window_empty_document.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>Empty document to test something in new window</title>
+</head>
+<body onload="opener.doTests(window);"></body>
+</html>
diff --git a/dom/events/test/window_wheel_default_action.html b/dom/events/test/window_wheel_default_action.html
new file mode 100644
index 0000000000..2353b64de3
--- /dev/null
+++ b/dom/events/test/window_wheel_default_action.html
@@ -0,0 +1,3618 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for default action of WheelEvent</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/SimpleTest/EventUtils.js"></script>
+ <script src="/tests/SimpleTest/paint_listener.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="scrollable" style="overflow: auto; width: 200px; height: 200px;">
+ <div id="clipper" style="margin: 0; padding: 0; overflow: hidden; width: 3000px; height: 3000px;">
+ <div id="scrolled" style="width: 5000px; height: 5000px;">
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text. Tere is a lot of text.<br>
+ </div>
+ </div>
+</div>
+<div id="spacerForBody"></div>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+// Until the first non-blank paint, the parent will set the opacity of our
+// browser to 0 using the 'blank' attribute.
+// Until the blank attribute is removed, we can't send scroll events.
+SimpleTest.waitForFocus(function() {
+ let chromeScript = SpecialPowers.loadChromeScript(_ => {
+ /* eslint-env mozilla/chrome-script */
+ let win = Services.wm.getMostRecentWindow("navigator:browser");
+ win.requestAnimationFrame(() => {
+ win.gBrowser.selectedBrowser.removeAttribute("blank");
+ win.requestAnimationFrame(() => {
+ sendAsyncMessage("blank-attribute-removed");
+ });
+ });
+ });
+ chromeScript.promiseOneMessage("blank-attribute-removed").then(() => {
+ chromeScript.destroy();
+ runTests();
+ });
+}, window);
+
+SimpleTest.requestLongerTimeout(6);
+SimpleTest.requestFlakyTimeout("untriaged");
+
+var winUtils = SpecialPowers.getDOMWindowUtils(window);
+// grab refresh driver
+winUtils.advanceTimeAndRefresh(100);
+
+var gScrollableElement = document.getElementById("scrollable");
+var gScrolledElement = document.getElementById("scrolled");
+var gSpacerForBodyElement = document.getElementById("spacerForBody");
+
+const kDefaultActionNone = 0;
+const kDefaultActionScroll = 1;
+const kDefaultActionHistory = 2;
+const kDefaultActionZoom = 3;
+const kDefaultActionHorizontalizedScroll = 4;
+
+const kDefaultActionOverrideXNoOverride = -1;
+const kDefaultActionOverrideXNone = kDefaultActionNone;
+const kDefaultActionOverrideXScroll = kDefaultActionScroll;
+const kDefaultActionOverrideXHistory = kDefaultActionHistory;
+const kDefaultActionOverrideXZoom = kDefaultActionZoom;
+
+// We sometimes rely on the scroll position not getting preserved across tests.
+function resetScrollPosition(element) {
+ element.style.display = "none";
+ element.offsetTop;
+ element.style.display = "";
+}
+
+function is()
+{
+ window.opener.is.apply(window.opener, arguments);
+}
+
+function ok()
+{
+ window.opener.ok.apply(window.opener, arguments);
+}
+
+function sendWheelAndWait(aX, aY, aEvent, aCallback)
+{
+ sendWheelAndPaint(gScrollableElement, aX, aY, aEvent, aCallback);
+}
+
+function hitEventLoop(aFunc, aTimes)
+{
+ winUtils.advanceTimeAndRefresh(100);
+
+ if (--aTimes) {
+ setTimeout(hitEventLoop, 0, aFunc, aTimes);
+ } else {
+ setTimeout(aFunc, 20);
+ }
+}
+
+function onZoomReset(aCallback) {
+ var fullZoom = SpecialPowers.getFullZoom(window);
+ if (fullZoom == 1) {
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+ // Zoom causes a resize of the viewport.
+ window.addEventListener("resize", function onResize(event) {
+ is(SpecialPowers.getFullZoom(window), 1, "Zoom should be reset to 1");
+ window.removeEventListener("resize", onResize);
+ SimpleTest.executeSoon(aCallback);
+ });
+}
+
+function setDeltaMultiplierSettings(aSettings, aCallback)
+{
+ SpecialPowers.pushPrefEnv({"set": [
+ ["mousewheel.default.delta_multiplier_x", aSettings.deltaMultiplierX * 100],
+ ["mousewheel.default.delta_multiplier_y", aSettings.deltaMultiplierY * 100],
+ ["mousewheel.default.delta_multiplier_z", aSettings.deltaMultiplierZ * 100],
+ ["mousewheel.with_alt.delta_multiplier_x", aSettings.deltaMultiplierX * 100],
+ ["mousewheel.with_alt.delta_multiplier_y", aSettings.deltaMultiplierY * 100],
+ ["mousewheel.with_alt.delta_multiplier_z", aSettings.deltaMultiplierZ * 100],
+ ["mousewheel.with_control.delta_multiplier_x", aSettings.deltaMultiplierX * 100],
+ ["mousewheel.with_control.delta_multiplier_y", aSettings.deltaMultiplierY * 100],
+ ["mousewheel.with_control.delta_multiplier_z", aSettings.deltaMultiplierZ * 100],
+ ["mousewheel.with_meta.delta_multiplier_x", aSettings.deltaMultiplierX * 100],
+ ["mousewheel.with_meta.delta_multiplier_y", aSettings.deltaMultiplierY * 100],
+ ["mousewheel.with_meta.delta_multiplier_z", aSettings.deltaMultiplierZ * 100],
+ ["mousewheel.with_shift.delta_multiplier_x", aSettings.deltaMultiplierX * 100],
+ ["mousewheel.with_shift.delta_multiplier_y", aSettings.deltaMultiplierY * 100],
+ ["mousewheel.with_shift.delta_multiplier_z", aSettings.deltaMultiplierZ * 100],
+ ]}, aCallback);
+}
+
+function doTestScroll(aSettings, aCallback)
+{
+ const kNoScroll = 0x00;
+ const kScrollUp = 0x01;
+ const kScrollDown = 0x02;
+ const kScrollLeft = 0x04;
+ const kScrollRight = 0x08;
+
+ const kTests = [
+ { description: "Scroll to bottom by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to bottom by pixel scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to top by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to top by pixel scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to right by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by pixel scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by pixel scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to bottom-right by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollRight },
+ { description: "Scroll to bottom-left by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollLeft },
+ { description: "Scroll to top-left by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollLeft },
+ { description: "Scroll to top-right by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollRight },
+ { description: "Not Scroll by pixel scroll for z",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+
+ { description: "Scroll to bottom by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to bottom by line scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to top by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to top by line scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to right by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by line scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by line scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to bottom-right by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollRight },
+ { description: "Scroll to bottom-left by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollLeft },
+ { description: "Scroll to top-left by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollLeft },
+ { description: "Scroll to top-right by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollRight },
+ { description: "Not Scroll by line scroll for z",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+
+ { description: "Scroll to bottom by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to bottom by page scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to top by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to top by page scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to right by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by page scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by page scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to bottom-right by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollRight },
+ { description: "Scroll to bottom-left by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollLeft },
+ { description: "Scroll to top-left by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollLeft },
+ { description: "Scroll to top-right by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollRight },
+ { description: "Not Scroll by page scroll for z",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+
+ // special cases.
+
+ // momentum scroll should cause scroll even if the action is zoom, but if the default action is none,
+ // shouldn't do it.
+ { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 1, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is -1, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 1, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is 0, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is -1, even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to bottom-right by momentum pixel scroll even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollRight },
+ { description: "Scroll to bottom-left by momentum pixel scroll even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollDown | kScrollLeft },
+ { description: "Scroll to top-left by momentum pixel scroll even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollLeft },
+ { description: "Scroll to top-right by momentum pixel scroll even if the action is zoom",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kScrollUp | kScrollRight },
+ { description: "Not Scroll by momentum pixel scroll for z (action is zoom)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Not Scroll by momentum pixel scroll if default action is none (action is zoom)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: true, altKey: false, metaKey: false },
+ expected: kNoScroll,
+ prepare (cb) { SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.action", 0]]}, cb); },
+ cleanup (cb) { SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.action", 1]]}, cb); } },
+
+ // momentum scroll should cause scroll even if the action is history, but if the default action is none,
+ // shouldn't do it.
+ { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to bottom by momentum pixel scroll when lineOrPageDelta is 1, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to top by momentum pixel scroll when lineOrPageDelta is -1, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by momentum pixel scroll when lineOrPageDelta is 1, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is 0, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by momentum pixel scroll when lineOrPageDelta is -1, even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to bottom-right by momentum pixel scroll even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollDown | kScrollRight },
+ { description: "Scroll to bottom-left by momentum pixel scroll even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollDown | kScrollLeft },
+ { description: "Scroll to top-left by momentum pixel scroll even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollUp | kScrollLeft },
+ { description: "Scroll to top-right by momentum pixel scroll even if the action is history",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kScrollUp | kScrollRight },
+ { description: "Not Scroll by momentum pixel scroll for z (action is history)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kNoScroll },
+ { description: "Not Scroll by momentum pixel scroll if default action is none (action is history)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: true,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: true, metaKey: false },
+ expected: kNoScroll,
+ prepare (cb) { SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.action", 0]]}, cb); },
+ cleanup (cb) { SpecialPowers.pushPrefEnv({"set": [["mousewheel.default.action", 1]]}, cb); } },
+
+ // Don't scroll along axis whose overflow style is hidden.
+ { description: "Scroll to only bottom by oblique pixel wheel event with overflow-x: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown,
+ prepare(cb) { gScrollableElement.style.overflowX = "hidden"; cb(); } },
+ { description: "Scroll to only bottom by oblique line wheel event with overflow-x: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to only bottom by oblique page wheel event with overflow-x: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to only top by oblique pixel wheel event with overflow-x: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: -1, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to only top by oblique line wheel event with overflow-x: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: -1, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to only top by oblique page wheel event with overflow-x: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: -1, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp,
+ cleanup (cb) { gScrollableElement.style.overflowX = "auto"; cb(); } },
+ { description: "Scroll to only right by oblique pixel wheel event with overflow-y: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight,
+ prepare(cb) { gScrollableElement.style.overflowY = "hidden"; cb(); } },
+ { description: "Scroll to only right by oblique line wheel event with overflow-y: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to only right by oblique page wheel event with overflow-y: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to only left by oblique pixel wheel event with overflow-y: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: -1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to only top by oblique line wheel event with overflow-y: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: -1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to only top by oblique page wheel event with overflow-y: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: -1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft,
+ cleanup (cb) { gScrollableElement.style.overflowY = "auto"; cb(); } },
+ { description: "Don't be scrolled by oblique pixel wheel event with overflow: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll,
+ prepare(cb) { gScrollableElement.style.overflow = "hidden"; cb(); } },
+ { description: "Don't be scrolled by oblique line wheel event with overflow: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't be scrolled by oblique page wheel event with overflow: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 1, expectedOverflowDeltaY: 1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't be scrolled by oblique pixel wheel event with overflow: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: -1, expectedOverflowDeltaY: -1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't be scrolled by oblique line wheel event with overflow: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: -1, expectedOverflowDeltaY: -1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't be scrolled by oblique page wheel event with overflow: hidden",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: -1, expectedOverflowDeltaY: -1,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll,
+ cleanup (cb) { gScrollableElement.style.overflow = "auto"; cb(); } },
+
+ // Don't scroll along axis whose overflow style is hidden and overflow delta values should
+ // be zero if there is ancestor scrollable element.
+ { description: "Scroll to only bottom by oblique pixel wheel event with overflow-x: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown,
+ prepare(cb) {
+ gScrollableElement.style.overflowX = "hidden";
+ gScrollableElement.style.position = "fixed";
+ gScrollableElement.style.top = "30px";
+ gScrollableElement.style.left = "30px";
+ // Make body element scrollable.
+ gSpacerForBodyElement.style.width = "5000px";
+ gSpacerForBodyElement.style.height = "5000px";
+ document.documentElement.scrollTop = 500;
+ document.documentElement.scrollLeft = 500;
+ cb();
+ } },
+ { description: "Scroll to only bottom by oblique line wheel event with overflow-x: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to only bottom by oblique page wheel event with overflow-x: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollDown },
+ { description: "Scroll to only top by oblique pixel wheel event with overflow-x: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to only top by oblique line wheel event with overflow-x: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp },
+ { description: "Scroll to only top by oblique page wheel event with overflow-x: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollUp,
+ cleanup (cb) { gScrollableElement.style.overflowX = "auto"; cb(); } },
+ { description: "Scroll to only right by oblique pixel wheel event with overflow-y: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0, deltaY: 16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight,
+ prepare(cb) { gScrollableElement.style.overflowY = "hidden"; cb(); } },
+ { description: "Scroll to only right by oblique line wheel event with overflow-y: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to only right by oblique page wheel event with overflow-y: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to only left by oblique pixel wheel event with overflow-y: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -16.0, deltaY: -16.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to only top by oblique line wheel event with overflow-y: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to only top by oblique page wheel event with overflow-y: hidden (body is scrollable)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: -1.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft,
+ cleanup (cb) {
+ gScrollableElement.style.overflowY = "auto";
+ gScrollableElement.style.position = "static";
+ gSpacerForBodyElement.style.width = "";
+ gSpacerForBodyElement.style.height = "";
+ cb();
+ } },
+ ];
+
+ var description;
+
+ var currentTestIndex = -1;
+ var isXReverted = (aSettings.deltaMultiplierX < 0);
+ var isYReverted = (aSettings.deltaMultiplierY < 0);
+
+ function doNextTest()
+ {
+ if (++currentTestIndex >= kTests.length) {
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+
+ var currentTest = kTests[currentTestIndex];
+ description = "doTestScroll(aSettings=" + aSettings.description + "), " + currentTest.description + ": ";
+ if (currentTest.prepare) {
+ // prepare() can make changes to a page such as
+ // changing the 'overflow' property which requires
+ // a repaint to take effect before sending
+ // scroll-wheel events.
+ currentTest.prepare(doWaitForPaintsAndScroll);
+ } else {
+ doTestCurrentScroll();
+ }
+ }
+
+ function doWaitForPaintsAndScroll() {
+ waitForAllPaintsFlushed(doTestCurrentScroll);
+ }
+
+ function doTestCurrentScroll() {
+ var currentTest = kTests[currentTestIndex];
+ sendWheelAndWait(10, 10, currentTest.event, function () {
+ if (currentTest.expected == kNoScroll) {
+ is(gScrollableElement.scrollTop, 1000, description + "scrolled vertical");
+ is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
+ } else {
+ var scrollUp = !isYReverted ? (currentTest.expected & kScrollUp) :
+ (currentTest.expected & kScrollDown);
+ var scrollDown = !isYReverted ? (currentTest.expected & kScrollDown) :
+ (currentTest.expected & kScrollUp);
+ if (scrollUp) {
+ ok(gScrollableElement.scrollTop < 1000, description + "not scrolled up, got " + gScrollableElement.scrollTop);
+ } else if (scrollDown) {
+ ok(gScrollableElement.scrollTop > 1000, description + "not scrolled down, got " + gScrollableElement.scrollTop);
+ } else {
+ is(gScrollableElement.scrollTop, 1000, description + "scrolled vertical");
+ }
+ var scrollLeft = !isXReverted ? (currentTest.expected & kScrollLeft) :
+ (currentTest.expected & kScrollRight);
+ var scrollRight = !isXReverted ? (currentTest.expected & kScrollRight) :
+ (currentTest.expected & kScrollLeft);
+ if (scrollLeft) {
+ ok(gScrollableElement.scrollLeft < 1000, description + "not scrolled to left, got " + gScrollableElement.scrollLeft);
+ } else if (scrollRight) {
+ ok(gScrollableElement.scrollLeft > 1000, description + "not scrolled to right, got " + gScrollableElement.scrollLeft);
+ } else {
+ is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
+ }
+ }
+ if (currentTest.cleanup) {
+ currentTest.cleanup(nextStep);
+ } else {
+ nextStep();
+ }
+
+ function nextStep() {
+ winUtils.advanceTimeAndRefresh(100);
+ doNextTest();
+ }
+ });
+ }
+ doNextTest();
+}
+
+function doTestHorizontalizedScroll(aSettings, aCallback)
+{
+ const kNoScroll = 0x00;
+ const kScrollLeft = 0x01;
+ const kScrollRight = 0x02;
+
+ const kTests = [
+ { description: "Scroll to right by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by pixel scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by pixel scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Don't scroll by deltaX (pixel scroll, lineOrPageDelta is 0)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by deltaX (pixel scroll, lineOrPageDelta is 1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by negative deltaX (pixel scroll, lineOrPageDelta is 0)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by negative deltaX (pixel scroll, lineOrPageDelta is -1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Scroll only to right by diagonal pixel scroll (to bottom-right)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll only to right by diagonal pixel scroll (to bottom-left)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll only to left by diagonal pixel scroll (to top-left)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll only to left by pixel scroll (to bottom-right)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Don't scroll by pixel scroll for z-axis",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+
+ { description: "Scroll to right by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by line scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by line scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Don't scroll by deltaX (line scroll, lineOrPageDelta is 0)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by deltaX (line scroll, lineOrPageDelta is 1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by negative deltaX (line scroll, lineOrPageDelta is 0)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by negative deltaY (line scroll, lineOrPageDelta is -1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Scroll only to right by diagonal line scroll (to bottom-right)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll only to right by diagonal line scroll (to bottom-left)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll only to left by diagonal line scroll (to top-left)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll only to left by line scroll (to top-right)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Don't scroll by line scroll for z-axis",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+
+ { description: "Scroll to right by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to right by page scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll to left by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll to left by page scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is 0)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is 1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is 0)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Don't scroll by deltaX (page scroll, lineOrPageDelta is -1)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ { description: "Scroll only to right by diagonal page scroll (to bottom-right)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll only to right by diagonal page scroll (to bottom-left)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollRight },
+ { description: "Scroll only to left by diagonal page scroll (to top-left)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Scroll only to left by diagonal page scroll (to top-right)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kScrollLeft },
+ { description: "Don't scroll by page scroll for z-axis",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: true, ctrlKey: false, altKey: false, metaKey: false },
+ expected: kNoScroll },
+ ];
+
+ var description;
+
+ var currentTestIndex = -1;
+ // deltaY should cause horizontal scroll and affected by deltaMultiplierY.
+ // So, horizontal scroll amount and direction is affected by deltaMultiplierY.
+ var isXReverted = (aSettings.deltaMultiplierY < 0);
+
+ function doNextTest()
+ {
+ if (++currentTestIndex >= kTests.length) {
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+
+ var currentTest = kTests[currentTestIndex];
+ description = "doTestHorizontalizedScroll(aSettings=" + aSettings.description + "), " + currentTest.description + ": ";
+ if (currentTest.prepare) {
+ currentTest.prepare(doTestCurrentScroll);
+ } else {
+ doTestCurrentScroll();
+ }
+ }
+
+ function doTestCurrentScroll() {
+ var currentTest = kTests[currentTestIndex];
+ sendWheelAndWait(10, 10, currentTest.event, function () {
+ is(gScrollableElement.scrollTop, 1000, description + "scrolled vertical");
+ if (currentTest.expected == kNoScroll) {
+ is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
+ } else {
+ var scrollLeft = !isXReverted ? (currentTest.expected & kScrollLeft) :
+ (currentTest.expected & kScrollRight);
+ var scrollRight = !isXReverted ? (currentTest.expected & kScrollRight) :
+ (currentTest.expected & kScrollLeft);
+ if (scrollLeft) {
+ ok(gScrollableElement.scrollLeft < 1000, description + "not scrolled to left, got " + gScrollableElement.scrollLeft);
+ } else if (scrollRight) {
+ ok(gScrollableElement.scrollLeft > 1000, description + "not scrolled to right, got " + gScrollableElement.scrollLeft);
+ } else {
+ is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
+ }
+ }
+ if (currentTest.cleanup) {
+ currentTest.cleanup(nextStep);
+ } else {
+ nextStep();
+ }
+
+ function nextStep() {
+ winUtils.advanceTimeAndRefresh(100);
+ doNextTest();
+ }
+ });
+ }
+ doNextTest();
+}
+
+// It will take *freaking* long time(maybe *hours*) to test all the writing mode
+// combinations for the scroll target and its root, because there are altogether
+// *one hundred* combinations (10 x 10)!
+//
+// So unless you really don't care a snap on time-consuming testing or a strict
+// criteria is required for testing, it is strongly recommeneded that you
+// comment out at least the writing modes which are marked as "peculiar" before
+// running this test, you are encouraged to also comment out those "uncommon"
+// writing modes in order to further shorten testing time.
+//
+// Note that if you are going to run time-consuming tests without commenting out
+// most of the writing modes, don't forget to increase the value of the
+// parameter in SimpleTest.requestLongerTimeout in this file; otherwise it'll
+// most likely lead you to a timed-out failure.
+//
+// Also note that |isBTT| has nothing to do with the behaviour of auto-dir
+// scrolling, it's just used to set the sign of |kOrigScrollTop|.
+const kWritingModes = [
+ {
+ isRTL: true,
+ isBTT: false,
+ styles: [
+ {
+ writingMode: "horizontal-tb",
+ direction: "rtl",
+ },
+ {
+ writingMode: "vertical-rl",
+ direction: "ltr",
+ },
+ // uncommon
+ //{
+ // writingMode: "sideways-rl",
+ // direction: "ltr",
+ //},
+ ],
+ },
+ {
+ isRTL: false,
+ isBTT: false,
+ styles: [
+ {
+ writingMode: "horizontal-tb",
+ direction: "ltr",
+ },
+ // uncommon
+ //{
+ // writingMode: "vertical-lr",
+ // direction: "ltr",
+ //},
+ // uncommon
+ //{
+ // writingMode: "sideways-lr",
+ // direction: "ltr",
+ //},
+ ],
+ },
+ {
+ isRTL: true,
+ isBTT: true,
+ styles: [
+ // peculiar
+ //{
+ // writingMode: "vertical-rl",
+ // direction: "rtl",
+ //},
+ // peculiar
+ //{
+ // writingMode: "sideways-rl",
+ // direction: "rtl",
+ //},
+ ],
+ },
+ {
+ isRTL: false,
+ isBTT: true,
+ styles: [
+ // peculiar
+ //{
+ // writingMode: "vertical-lr",
+ // direction: "rtl",
+ //},
+ // peculiar
+ //{
+ // writingMode: "sideways-lr",
+ // direction: "rtl",
+ //},
+ ],
+ },
+];
+
+function getFirstWritingModeStyle()
+{
+ if (kWritingModes.length < 1) {
+ return false;
+ }
+ let typeIndex = 0;
+ while (!kWritingModes[typeIndex].styles.length) {
+ typeIndex++;
+ if (typeIndex >= kWritingModes.length) {
+ return false;
+ }
+ }
+ return {typeIndex, styleIndex: 0};
+}
+
+function getNextWritingModeStyle(curStyle)
+{
+ let typeIndex = curStyle.typeIndex;
+ let styleIndex = curStyle.styleIndex + 1;
+ while (typeIndex < kWritingModes.length) {
+ if (styleIndex < kWritingModes[typeIndex].styles.length) {
+ return {typeIndex, styleIndex};
+ }
+ typeIndex++;
+ styleIndex = 0;
+ }
+ return false;
+}
+
+function doTestAutoDirScroll(aSettings, aAutoDirTrait, aCallback)
+{
+ // Go through all the writing-mode combinations for the scroll target and its
+ // root.
+
+ let firstStyle = getFirstWritingModeStyle();
+ if (!firstStyle) {
+ // The whole writing mode list is empty, no need to do any test for auto-dir
+ // scrolling. Go ahead with the subsequent tests.
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+
+ // Begin with the first style for both the root and the scroll target.
+ // doTestAutoDirScroll2 will recursively call itself back for every
+ // style combination with getNextWritingModeStyle until all combinations have
+ // been enumerated, and then it will call SimpleTest.executeSoon(aCallback).
+ doTestAutoDirScroll2(aSettings, aAutoDirTrait,
+ firstStyle, firstStyle,
+ aCallback);
+}
+
+function doTestAutoDirScroll2(aSettings, aAutoDirTrait,
+ aStyleForRoot, aStyleForTarget,
+ aCallback)
+{
+ const kStyleTypeForRoot = kWritingModes[aStyleForRoot.typeIndex];
+ const kStyleTypeForTarget = kWritingModes[aStyleForTarget.typeIndex];
+
+ const kStyleForRoot = kStyleTypeForRoot.styles[aStyleForRoot.styleIndex];
+ const kStyleForTarget = kStyleTypeForTarget.styles[aStyleForTarget.styleIndex];
+
+ const kIsRootRTL = kStyleTypeForRoot.isRTL;
+ const kIsTargetRTL = kStyleTypeForTarget.isRTL;
+ // Just used to set the sign of |kOrigScrollTop|, not related to the auto-dir
+ // behaviour.
+ const kIsTargetBTT = kStyleTypeForTarget.isBTT;
+
+ const kOldStyleForRoot = {};
+ const kOldStyleForTarget = {};
+
+ const kHonoursRoot = Boolean(aAutoDirTrait.honoursRoot);
+
+ const kNoScroll = 0x00;
+ const kScrollUp = 0x01;
+ const kScrollDown = 0x02;
+ const kScrollLeft = 0x04;
+ const kScrollRight = 0x08;
+
+ // The four constants indicate the expected result if the scroll direction is
+ // adjusted.
+ const kAdjustedForUp = {};
+ const kAdjustedForDown = {};
+ const kAdjustedForLeft = {};
+ const kAdjustedForRight = {};
+ if (kHonoursRoot ? kIsRootRTL : kIsTargetRTL) {
+ kAdjustedForUp.result = kScrollRight;
+ kAdjustedForUp.desc = "right";
+ kAdjustedForDown.result = kScrollLeft;
+ kAdjustedForDown.desc = "left";
+ kAdjustedForLeft.result = kScrollDown;
+ kAdjustedForLeft.desc = "bottom";
+ kAdjustedForRight.result = kScrollUp;
+ kAdjustedForRight.desc = "top";
+ } else {
+ kAdjustedForUp.result = kScrollLeft;
+ kAdjustedForUp.desc = "left";
+ kAdjustedForDown.result = kScrollRight;
+ kAdjustedForDown.desc = "right";
+ kAdjustedForLeft.result = kScrollUp;
+ kAdjustedForLeft.desc = "top";
+ kAdjustedForRight.result = kScrollDown;
+ kAdjustedForRight.desc = "bottom";
+ }
+
+ const kTests = [
+ // Tests: Test pixel scrolling towards four edges when the target
+ // overflows in both the two directions.
+ // Results: All are unadjusted.
+ // Reason: Auto-dir adjustment never applies to a target which overflows in
+ // both the two directions.
+ { description: "auto-dir scroll to bottom by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown,
+ prepare (cb) {
+ // Static contents will not start from the topleft side in some
+ // writing modes, for ease of coding, we simply absolutely
+ // position the target to the topleft in every case.
+ gScrollableElement.style.position = "absolute";
+ gScrollableElement.style.top = "10px";
+ gScrollableElement.style.left = "10px";
+ SpecialPowers.pushPrefEnv({
+ "set": [["mousewheel.autodir.enabled", true],
+ ["mousewheel.autodir.honourroot", kHonoursRoot]]
+ }, cb);
+ } },
+ { description: "auto-dir scroll to bottom by pixel scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to top by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to top by pixel scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to right by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to right by pixel scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to left by pixel scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "auto-dir scroll to left by pixel scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+
+ // Tests: Test diagonal pixel scrolling when the target overflows in both
+ // the two directions.
+ // Results: All are unadjusted.
+ // Reason: Auto-dir adjustment never applies to a target which overflows in
+ // both the two directions, furthermore, it never applies to
+ // diagonal scrolling.
+ { description: "auto-dir scroll to bottom-right by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown | kScrollRight },
+ { description: "auto-dir scroll to bottom-left by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown | kScrollLeft },
+ { description: "auto-dir scroll to top-left by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp | kScrollLeft },
+ { description: "auto-dir scroll to top-right by pixel scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp | kScrollRight },
+
+ // Tests: Test line scrolling towards four edges when the target overflows
+ // in both the two directions.
+ // Results: All are unadjusted.
+ // Reason: Auto-dir adjustment never applies to a target which overflows in
+ // both the two directions.
+ { description: "auto-dir scroll to bottom by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to bottom by line scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to top by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to top by line scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to right by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to right by line scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to left by line scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "auto-dir scroll to left by line scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+
+ // Tests: Test diagonal line scrolling when the target overflows in both
+ // the two directions.
+ // Results: All are unadjusted.
+ // Reason: Auto-dir adjustment never applies to a target which overflows in
+ // both the two directions, furthermore, it never applies to
+ // diagonal scrolling.
+ { description: "auto-dir scroll to bottom-right by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown | kScrollRight },
+ { description: "auto-dir scroll to bottom-left by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown | kScrollLeft },
+ { description: "auto-dir scroll to top-left by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp | kScrollLeft },
+ { description: "auto-dir scroll to top-right by line scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp | kScrollRight },
+
+ // Tests: Test page scrolling towards four edges when the target overflows
+ // in both the two directions.
+ // Results: All are unadjusted.
+ // Reason: Auto-dir adjustment never applies to a target which overflows in
+ // both the two directions.
+ { description: "auto-dir scroll to bottom by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to bottom by page scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to top by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to top by page scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to right by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to right by page scroll when lineOrPageDelta is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to left by page scroll even if lineOrPageDelta is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "auto-dir scroll to left by page scroll when lineOrPageDelta is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+
+ // Tests: Test diagonal page scrolling when the target overflows in both
+ // the two directions.
+ // Results: All are unadjusted.
+ // Reason: Auto-dir adjustment never applies to a target which overflows in
+ // both the two directions, furthermore, it never applies to
+ // diagonal scrolling.
+ { description: "auto-dir scroll to bottom-right by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown | kScrollRight },
+ { description: "auto-dir scroll to bottom-left by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown | kScrollLeft },
+ { description: "auto-dir scroll to top-left by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp | kScrollLeft },
+ { description: "auto-dir scroll to top-right by page scroll",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp | kScrollRight },
+
+ // The tests above in this function are all for testing a target with two
+ // scrollbars. All of them should not be adjustable.
+ // From here on, the tests below in this function are all for testing a
+ // target with only one scrollbar, either a vertical scrollbar or horizontal
+ // scrollbar. Some of them are adjustable.
+
+ // Tests: Test pixel scrolling towards four edges when the target
+ // overflows only in the horizontal direction.
+ // Results: Vertical wheel scrolls are adjusted to be horizontal whereas the
+ // horizontal wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "auto";
+ gScrollableElement.style.overflowY = "hidden";
+ resetScrollPosition(gScrollableElement);
+ cb();
+ } },
+ { description: "auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by pixel scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result },
+ { description: "auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by pixel scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "auto-dir scroll to right by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to right by pixel scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to left by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "auto-dir scroll to left by pixel scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+
+ // Tests: Test pixel scrolling towards four edges when the target
+ // overflows only in the vertical direction.
+ // Results: Horizontal wheel scrolls are adjusted to be vertical whereas the
+ // vertical wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "auto-dir scroll to bottom by pixel scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "hidden";
+ gScrollableElement.style.overflowY = "auto";
+ resetScrollPosition(gScrollableElement);
+ cb();
+ } },
+ { description: "auto-dir scroll to bottom by pixel scroll when lineOrPageDelta is 1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to top by pixel scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to top by pixel scroll when lineOrPageDelta is -1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to " + kAdjustedForRight.desc +
+ "(originally right) by pixel scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForRight.result },
+ { description: "auto-dir scroll to " + kAdjustedForRight.desc +
+ "(originally right) by pixel scroll when lineOrPageDelta is 1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForRight.result },
+ { description: "auto-dir scroll to " + kAdjustedForLeft.desc +
+ "(originally left) by pixel scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForLeft.result },
+ { description: "auto-dir scroll to " + kAdjustedForLeft.desc +
+ "(originally left) by pixel scroll when lineOrPageDelta is -1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForLeft.result },
+
+ // Tests: Test line scrolling towards four edges when the target overflows
+ // only in the horizontal direction.
+ // Results: Vertical wheel scrolls are adjusted to be horizontal whereas the
+ // horizontal wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by line scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "auto";
+ gScrollableElement.style.overflowY = "hidden";
+ resetScrollPosition(gScrollableElement);
+ cb();
+ } },
+ { description: "auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by line scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result },
+ { description: "auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by line scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by line scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "auto-dir scroll to right by line scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to right by line scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to left by line scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "auto-dir scroll to left by line scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+
+ // Tests: Test line scrolling towards four edges when the target overflows
+ // only in the vertical direction.
+ // Results: Horizontal wheel scrolls are adjusted to be vertical whereas the
+ // vertical wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "auto-dir scroll to bottom by line scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "hidden";
+ gScrollableElement.style.overflowY = "auto";
+ resetScrollPosition(gScrollableElement);
+ cb();
+ } },
+ { description: "auto-dir scroll to bottom by line scroll when lineOrPageDelta is 1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to top by line scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to top by line scroll when lineOrPageDelta is -1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to " + kAdjustedForRight.desc +
+ "(originally right) by line scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForRight.result },
+ { description: "auto-dir scroll to " + kAdjustedForRight.desc +
+ "(originally right) by line scroll when lineOrPageDelta is 1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForRight.result },
+ { description: "auto-dir scroll to " + kAdjustedForLeft.desc +
+ "(originally left) by line scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForLeft.result },
+ { description: "auto-dir scroll to " + kAdjustedForLeft.desc +
+ "(originally left) by line scroll when lineOrPageDelta is -1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForLeft.result },
+
+ // Tests: Test page scrolling towards four edges when the target overflows
+ // only in the horizontal direction.
+ // Results: Vertical wheel scrolls are adjusted to be horizontal whereas the
+ // horizontal wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by page scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "auto";
+ gScrollableElement.style.overflowY = "hidden";
+ resetScrollPosition(gScrollableElement);
+ cb();
+ } },
+ { description: "auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by page scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result },
+ { description: "auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by page scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by page scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "auto-dir scroll to right by page scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to right by page scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "auto-dir scroll to left by page scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "auto-dir scroll to left by page scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+
+ // Tests: Test page scrolling towards four edges when the target overflows
+ // only in the vertical direction.
+ // Results: Horizontal wheel scrolls are adjusted to be vertical whereas the
+ // vertical wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "auto-dir scroll to bottom by page scroll when lineOrPageDelta is 1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "hidden";
+ gScrollableElement.style.overflowY = "auto";
+ resetScrollPosition(gScrollableElement);
+ cb();
+ } },
+ { description: "auto-dir scroll to bottom by page scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollDown },
+ { description: "auto-dir scroll to top by page scroll when lineOrPageDelta is -1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to top by page scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollUp },
+ { description: "auto-dir scroll to " + kAdjustedForRight.desc +
+ "(originally right) by page scroll when lineOrPageDelta is 1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForRight.result },
+ { description: "auto-dir scroll to " + kAdjustedForRight.desc +
+ "(originally right) by page scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForRight.result },
+ { description: "auto-dir scroll to " + kAdjustedForLeft.desc +
+ "(originally left) by page scroll when lineOrPageDelta is -1, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForLeft.result },
+ { description: "auto-dir scroll to " + kAdjustedForLeft.desc +
+ "(originally left) by page scroll even if lineOrPageDelta is 0, " +
+ "no horizontal scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForLeft.result,
+ cleanup (cb) {
+ SpecialPowers.pushPrefEnv({"set":
+ [["mousewheel.autodir.enabled",
+ false]]},
+ cb);
+ } },
+ // Tests: Test that autodir scrolling can be force-enabled using windowUtils.
+ // This only tests vertical wheel scrolls being adjusted to be
+ // horizontal, rather than re-testing all autodir behaviours just for
+ // this way of enabling it.
+ // Results: Vertical wheel scrolls are adjusted to be horizontal whereas the
+ // horizontal wheel scrolls are unadjusted.
+ // Reason: Auto-dir adjustment applies to a target if the target overflows
+ // in only one direction and the direction is orthogonal to the
+ // wheel and deltaZ is zero.
+ { description: "force-enabled auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result,
+ prepare (cb) {
+ gScrollableElement.style.overflowX = "auto";
+ gScrollableElement.style.overflowY = "hidden";
+ resetScrollPosition(gScrollableElement);
+ winUtils.setMousewheelAutodir(gScrollableElement, true, kHonoursRoot)
+ cb();
+ } },
+ { description: "force-enabled auto-dir scroll to " + kAdjustedForDown.desc +
+ "(originally bottom) by pixel scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForDown.result },
+ { description: "force-enabled auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "force-enabled auto-dir scroll to " + kAdjustedForUp.desc +
+ "(originally top) by pixel scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: true,
+ expected: kAdjustedForUp.result },
+ { description: "force-enabled auto-dir scroll to right by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "force-enabled auto-dir scroll to right by pixel scroll when lineOrPageDelta is 1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollRight },
+ { description: "force-enabled auto-dir scroll to left by pixel scroll even if lineOrPageDelta is 0, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft },
+ { description: "force-enabled auto-dir scroll to left by pixel scroll when lineOrPageDelta is -1, " +
+ "no vertical scrollbar",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0, isMomentum: false,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0,
+ shiftKey: false, ctrlKey: false, altKey: false, metaKey: false },
+ adjusted: false,
+ expected: kScrollLeft,
+ cleanup (cb) {
+ gScrollableElement.style.position = "static";
+ gScrollableElement.style.top = "auto";
+ gScrollableElement.style.left = "auto";
+ gScrollableElement.style.overflow = "auto";
+ Object.assign(document.body.style, kOldStyleForRoot);
+ Object.assign(gScrollableElement.style, kOldStyleForTarget);
+ winUtils.setMousewheelAutodir(gScrollableElement, false, false);
+ cb();
+ } },
+ ];
+
+ let styleDescForRoot = "";
+ let styleDescForTarget = "";
+ Object.keys(kStyleForRoot).forEach(function(property)
+ {
+ kOldStyleForRoot[property] = document.body.style[property];
+ document.body.style[property] = kStyleForRoot[property];
+ if ("" !== styleDescForRoot) {
+ styleDescForRoot += " ";
+ }
+ styleDescForRoot += property + ": " + kStyleForRoot[property] + ";";
+ });
+ Object.keys(kStyleForTarget).forEach(function(property)
+ {
+ kOldStyleForTarget[property] = gScrollableElement.style[property];
+ gScrollableElement.style[property] = kStyleForTarget[property];
+ if ("" !== styleDescForTarget) {
+ styleDescForTarget += " ";
+ }
+ styleDescForTarget += property + ": " +
+ kStyleForTarget[property] + ";";
+ });
+
+ let description;
+ let currentTestIndex = -1;
+ let isXReverted = aSettings.deltaMultiplierX < 0;
+ let isYReverted = aSettings.deltaMultiplierY < 0;
+
+ // We are doing a "trick" here:
+ // If the `kHonoursRoot` is true and the scroll target and the root's contents
+ // are both LTR or both RTL, we can safely skip these tests, because the same
+ // behavior is tested when the `kHonoursRoot` is false.
+ if (kHonoursRoot && kIsRootRTL === kIsTargetRTL) {
+ currentTestIndex = kTests.length;
+ }
+
+ const kOrigScrollLeft = kIsTargetRTL ? -1000 : 1000;
+ const kOrigScrollTop = kIsTargetBTT ? -1000 : 1000;
+
+ function doNextTest()
+ {
+ if (++currentTestIndex >= kTests.length) {
+ // The tests for the current writing mode combination have been finished.
+ // Continue the tests for the next writing mode combination, if any.
+ let nextStyleForRoot;
+ let nextStyleForTarget;
+ nextStyleForTarget = getNextWritingModeStyle(aStyleForTarget);
+ if (nextStyleForTarget) {
+ nextStyleForRoot = aStyleForRoot;
+ } else {
+ nextStyleForRoot = getNextWritingModeStyle(aStyleForRoot);
+ if (!nextStyleForRoot) {
+ // All writing mode combinations have been enumerated, so stop
+ // recursively calling doTestAutoDirScroll2, and go ahead with the
+ // subsequent tests.
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+ nextStyleForTarget = getFirstWritingModeStyle();
+ }
+ doTestAutoDirScroll2(aSettings, aAutoDirTrait,
+ nextStyleForRoot, nextStyleForTarget,
+ aCallback);
+ return;
+ }
+
+ gScrollableElement.scrollTop = kOrigScrollTop;
+ gScrollableElement.scrollLeft = kOrigScrollLeft;
+
+ var currentTest = kTests[currentTestIndex];
+ description = "doTestAutoDirScroll(aSettings=" + aSettings.description + ", ";
+ if (kHonoursRoot) {
+ description += "{honoursRoot: true}), ";
+ } else {
+ description += "{honoursRoot: false}), ";
+ }
+ description += "root = " + styleDescForRoot + " ";
+ description += "target = " + styleDescForTarget + " ";
+ if (currentTest.adjusted) {
+ description += "adjusted ";
+ } else {
+ description += "unadjusted ";
+ }
+ description += currentTest.description + ": ";
+ if (currentTest.prepare) {
+ currentTest.prepare(doTestCurrentScroll);
+ } else {
+ doTestCurrentScroll();
+ }
+ }
+
+ function doTestCurrentScroll() {
+ var currentTest = kTests[currentTestIndex];
+ sendWheelAndWait(100, 100, currentTest.event, function () {
+ if (currentTest.expected == kNoScroll) {
+ is(gScrollableElement.scrollTop, kOrigScrollTop, description + "scrolled vertical");
+ is(gScrollableElement.scrollLeft, kOrigScrollLeft, description + "scrolled horizontal");
+ } else {
+ // If auto-dir adjustment occurs, temporarily swap |isYReverted| and
+ // |isXReverted|.
+ if (currentTest.adjusted) {
+ [isYReverted, isXReverted] = [isXReverted, isYReverted];
+ }
+ let scrollUp = !isYReverted ? (currentTest.expected & kScrollUp) :
+ (currentTest.expected & kScrollDown);
+ let scrollDown = !isYReverted ? (currentTest.expected & kScrollDown) :
+ (currentTest.expected & kScrollUp);
+ if (scrollUp) {
+ ok(gScrollableElement.scrollTop < kOrigScrollTop,
+ description + "not scrolled up, got " + gScrollableElement.scrollTop);
+ } else if (scrollDown) {
+ ok(gScrollableElement.scrollTop > kOrigScrollTop,
+ description + "not scrolled down, got " + gScrollableElement.scrollTop);
+ } else {
+ is(gScrollableElement.scrollTop, kOrigScrollTop,
+ description + "scrolled vertical");
+ }
+ var scrollLeft = !isXReverted ? (currentTest.expected & kScrollLeft) :
+ (currentTest.expected & kScrollRight);
+ var scrollRight = !isXReverted ? (currentTest.expected & kScrollRight) :
+ (currentTest.expected & kScrollLeft);
+ if (scrollLeft) {
+ ok(gScrollableElement.scrollLeft < kOrigScrollLeft,
+ description + "not scrolled to left, got " + gScrollableElement.scrollLeft);
+ } else if (scrollRight) {
+ ok(gScrollableElement.scrollLeft > kOrigScrollLeft,
+ description + "not scrolled to right, got " + gScrollableElement.scrollLeft);
+ } else {
+ is(gScrollableElement.scrollLeft, kOrigScrollLeft,
+ description + "scrolled horizontal");
+ }
+ // |isYReverted| and |isXReverted| have been temporarily swaped for
+ // auto-dir adjustment, restore them.
+ if (currentTest.adjusted) {
+ [isYReverted, isXReverted] = [isXReverted, isYReverted];
+ }
+ }
+ if (currentTest.cleanup) {
+ currentTest.cleanup(nextStep);
+ } else {
+ nextStep();
+ }
+
+ function nextStep() {
+ winUtils.advanceTimeAndRefresh(100);
+ doNextTest();
+ }
+ });
+ }
+ doNextTest();
+}
+
+function doTestZoom(aSettings, aCallback)
+{
+ if ((aSettings.deltaMultiplierX != 1.0 && aSettings.deltaMultiplierX != -1.0) ||
+ (aSettings.deltaMultiplierY != 1.0 && aSettings.deltaMultiplierY != -1.0)) {
+ todo(false, "doTestZoom doesn't support to test with aSettings=" + aSettings.description);
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+
+ const kNone = 0x00;
+ const kPositive = 0x01;
+ const kNegative = 0x02;
+ const kUseX = 0x10;
+ const kUseY = 0x20;
+ const kTests = [
+ { description: "by vertical/positive pixel event when its lineOrPageDeltaY is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by vertical/positive pixel event when its lineOrPageDeltaY is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kPositive | kUseY },
+ { description: "by vertical/negative pixel event when its lineOrPageDeltaY is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by vertical/negative pixel event when its lineOrPageDeltaY is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -8.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNegative | kUseY },
+ { description: "by horizotal/positive pixel event when its lineOrPageDeltaX is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by horizotal/positive pixel event when its lineOrPageDeltaX is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kPositive | kUseX },
+ { description: "by horizotal/negative pixel event when its lineOrPageDeltaX is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by horizotal/negative pixel event when its lineOrPageDeltaX is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -8.0, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNegative | kUseX },
+ { description: "by z pixel event",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 16.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+
+ { description: "by vertical/positive line event when its lineOrPageDeltaY is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by vertical/positive line event when its lineOrPageDeltaY is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kPositive | kUseY },
+ { description: "by vertical/negative line event when its lineOrPageDeltaY is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by vertical/negative line event when its lineOrPageDeltaY is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNegative | kUseY },
+ { description: "by horizotal/positive line event when its lineOrPageDeltaX is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by horizotal/positive line event when its lineOrPageDeltaX is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kPositive | kUseX },
+ { description: "by horizotal/negative line event when its lineOrPageDeltaX is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by horizotal/negative line event when its lineOrPageDeltaX is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNegative | kUseX },
+ { description: "by z line event",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+
+ { description: "by vertical/positive page event when its lineOrPageDeltaY is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by vertical/positive page event when its lineOrPageDeltaY is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kPositive | kUseY },
+ { description: "by vertical/negative page event when its lineOrPageDeltaY is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by vertical/negative page event when its lineOrPageDeltaY is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -0.5, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNegative | kUseY },
+ { description: "by horizotal/positive page event when its lineOrPageDeltaX is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by horizotal/positive page event when its lineOrPageDeltaX is 1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kPositive | kUseX },
+ { description: "by horizotal/negative page event when its lineOrPageDeltaX is 0",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ { description: "by horizotal/negative page event when its lineOrPageDeltaX is -1",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -0.5, deltaY: 0.0, deltaZ: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNegative | kUseX },
+ { description: "by z page event",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 0.0, deltaZ: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0,
+ expectedOverflowDeltaX: 0, expectedOverflowDeltaY: 0 },
+ expected: kNone },
+ ];
+
+ var description, currentTest;
+ var currentTestIndex = -1;
+ var isXReverted = (aSettings.deltaMultiplierX < 0);
+ var isYReverted = (aSettings.deltaMultiplierY < 0);
+
+ function doNextTest() {
+ if (++currentTestIndex >= kTests.length) {
+ SimpleTest.executeSoon(aCallback);
+ return;
+ }
+
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+
+ currentTest = kTests[currentTestIndex];
+ description = "doTestZoom(aSettings=" + aSettings.description + "), ";
+ if (currentTest.expected == kNone) {
+ description += "Shouldn't ";
+ } else {
+ description += "Should ";
+ }
+ description += "zoom " + currentTest.description + ": ";
+
+ var event = currentTest.event;
+ event.ctrlKey = true;
+
+ // NOTE: Zooming might change scrollTop and scrollLeft by rounding fraction.
+ // This test assume that zoom happens synchronously and scrolling
+ // happens asynchronously.
+ var scrollTop = gScrollableElement.scrollTop;
+ var scrollLeft = gScrollableElement.scrollLeft;
+
+ fullZoomChangePromise = new Promise(resolve => {
+ if (currentTest.expected & (kNegative | kPositive)) {
+ // Zoom causes a resize of the viewport.
+ window.addEventListener("resize", function onResize() {
+ if (SpecialPowers.getFullZoom(window) != 1) {
+ window.removeEventListener("resize", onResize);
+ setTimeout(() => resolve(), 0);
+ }
+ });
+ } else {
+ resolve();
+ }
+ });
+
+ sendWheelAndWait(10, 10, event, function () {
+ is(gScrollableElement.scrollTop, scrollTop, description + "scrolled vertical");
+ is(gScrollableElement.scrollLeft, scrollLeft, description + "scrolled horizontal");
+
+ fullZoomChangePromise.then(() => {
+ // When input event prioritization is enabled, the wheel event may be
+ // dispatched to the content process before the message 'FullZoom' to
+ // zoom in/out. Waiting for the event 'FullZoomChange' and then check
+ // the result.
+ if (!(currentTest.expected & (kNegative | kPositive))) {
+ is(SpecialPowers.getFullZoom(window), 1.0, description + "zoomed");
+ } else {
+ var isReverted = (currentTest.expected & kUseX) ? isXReverted :
+ (currentTest.expected & kUseY) ? isYReverted : false;
+ if ((!isReverted && (currentTest.expected & kNegative)) ||
+ (isReverted && (currentTest.expected & kPositive))) {
+ ok(SpecialPowers.getFullZoom(window) > 1.0,
+ description + "not zoomed in, got " + SpecialPowers.getFullZoom(window));
+ } else {
+ ok(SpecialPowers.getFullZoom(window) < 1.0,
+ description + "not zoomed out, got " + SpecialPowers.getFullZoom(window));
+ }
+ }
+ if (SpecialPowers.getFullZoom(window) != 1) {
+ // Only synthesizes key event to reset zoom when necessary to avoid
+ // triggering the next test before the key event is handled. In that
+ // case, the key event may break the next test.
+ synthesizeKey("0", { accelKey: true });
+ }
+ onZoomReset(function () {
+ hitEventLoop(doNextTest, 20);
+ });
+ });
+ });
+ }
+ doNextTest();
+}
+
+function doTestZoomedScroll(aCallback)
+{
+ var zoom = 1.0;
+ function setFullZoom(aWindow, aZoom)
+ {
+ zoom = aZoom;
+ SpecialPowers.setFullZoom(aWindow, aZoom);
+ }
+
+ function prepareTestZoomedPixelScroll()
+ {
+ // Reset zoom and store the scroll amount into the data.
+ synthesizeKey("0", { accelKey: true });
+ zoom = 1.0;
+ onZoomReset(testZoomedPixelScroll);
+ }
+
+ function testZoomedPixelScroll()
+ {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ // Ensure not to be in reflow.
+ hitEventLoop(function () {
+ function mousePixelScrollHandler(aEvent)
+ {
+ if (aEvent.axis == MouseScrollEvent.HORIZONTAL_AXIS) {
+ is(aEvent.detail, 16,
+ "doTestZoomedScroll: The detail of horizontal MozMousePixelScroll for pixel wheel event is wrong");
+ } else if (aEvent.axis == MouseScrollEvent.VERTICAL_AXIS) {
+ is(aEvent.detail, 16,
+ "doTestZoomedScroll: The detail of vertical MozMousePixelScroll for pixel wheel event is wrong");
+ } else {
+ ok(false, "doTestZoomedScroll: The axis of MozMousePixelScroll for pixel wheel event is invalid, got " + aEvent.axis);
+ }
+ }
+ function wheelHandler(aEvent)
+ {
+ is(aEvent.deltaX, 16.0 / zoom,
+ "doTestZoomedScroll: The deltaX of wheel for pixel is wrong");
+ is(aEvent.deltaY, 16.0 / zoom,
+ "doTestZoomedScroll: The deltaY of wheel for pixel is wrong");
+ }
+ window.addEventListener("MozMousePixelScroll", mousePixelScrollHandler, true);
+ window.addEventListener("wheel", wheelHandler, true);
+ var event = {
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0,
+ deltaY: 16.0,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 0
+ };
+ // wait scrolled actually.
+ sendWheelAndWait(10, 10, event, function () {
+ var scrolledX = gScrollableElement.scrollLeft;
+ var scrolledY = gScrollableElement.scrollTop;
+ ok(scrolledX > 1000,
+ "doTestZoomedScroll: scrolledX must be larger than 1000 for pixel wheel event, got " + scrolledX);
+ ok(scrolledY > 1000,
+ "doTestZoomedScroll: scrolledY must be larger than 1000 for pixel wheel event, got " + scrolledY);
+
+ // Zoom
+ setFullZoom(window, 2.0);
+ // Ensure not to be in reflow.
+ hitEventLoop(function () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ var evt = {
+ deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 16.0,
+ deltaY: 16.0,
+ lineOrPageDeltaX: 0,
+ lineOrPageDeltaY: 0
+ };
+ // wait scrolled actually.
+ sendWheelAndWait(10, 10, evt, function () {
+ ok(Math.abs(gScrollableElement.scrollLeft - (1000 + (scrolledX - 1000) / 2)) <= 1,
+ "doTestZoomedScroll: zoomed horizontal scroll amount by pixel wheel event is different from normal, scrollLeft=" +
+ gScrollableElement.scrollLeft + ", scrolledX=" + scrolledX);
+ ok(Math.abs(gScrollableElement.scrollTop - (1000 + (scrolledY - 1000) / 2)) <= 1,
+ "doTestZoomedScroll: zoomed vertical scroll amount by pixel wheel event is different from normal, scrollTop=" +
+ gScrollableElement.scrollTop + ", scrolledY=" + scrolledY);
+ window.removeEventListener("MozMousePixelScroll", mousePixelScrollHandler, true);
+ window.removeEventListener("wheel", wheelHandler, true);
+
+ synthesizeKey("0", { accelKey: true });
+ onZoomReset(prepareTestZoomedLineScroll);
+ });
+ }, 20);
+ });
+ }, 20);
+ }
+
+ function prepareTestZoomedLineScroll()
+ {
+ // Reset zoom and store the scroll amount into the data.
+ synthesizeKey("0", { accelKey: true });
+ zoom = 1.0;
+ onZoomReset(testZoomedLineScroll);
+ }
+ function testZoomedLineScroll()
+ {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ // Ensure not to be in reflow.
+ hitEventLoop(function () {
+ var lineHeightX, lineHeightY;
+ function handler(aEvent)
+ {
+ if (aEvent.axis == MouseScrollEvent.HORIZONTAL_AXIS) {
+ if (lineHeightX == undefined) {
+ lineHeightX = aEvent.detail;
+ } else {
+ ok(Math.abs(aEvent.detail - lineHeightX) <= 1,
+ "doTestZoomedScroll: The detail of horizontal MozMousePixelScroll for line wheel event is wrong, aEvent.detail=" +
+ aEvent.detail + ", lineHeightX=" + lineHeightX);
+ }
+ } else if (aEvent.axis == MouseScrollEvent.VERTICAL_AXIS) {
+ if (lineHeightY == undefined) {
+ lineHeightY = aEvent.detail;
+ } else {
+ ok(Math.abs(aEvent.detail - lineHeightY) <= 1,
+ "doTestZoomedScroll: The detail of vertical MozMousePixelScroll for line wheel event is wrong, aEvent.detail=" +
+ aEvent.detail + ", lineHeightY=" + lineHeightY);
+ }
+ } else {
+ ok(false, "doTestZoomedScroll: The axis of MozMousePixelScroll for line wheel event is invalid, got " + aEvent.axis);
+ }
+ }
+ window.addEventListener("MozMousePixelScroll", handler, true);
+ var event = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0,
+ deltaY: 1.0,
+ lineOrPageDeltaX: 1,
+ lineOrPageDeltaY: 1
+ };
+ // wait scrolled actually.
+ sendWheelAndWait(10, 10, event, function () {
+ var scrolledX = gScrollableElement.scrollLeft;
+ var scrolledY = gScrollableElement.scrollTop;
+ ok(scrolledX > 1000,
+ "doTestZoomedScroll: scrolledX must be larger than 1000 for line wheel event, got " + scrolledX);
+ ok(scrolledY > 1000,
+ "doTestZoomedScroll: scrolledY must be larger than 1000 for line wheel event, got " + scrolledY);
+
+ // Zoom
+ setFullZoom(window, 2.0);
+ // Ensure not to be in reflow.
+ hitEventLoop(function () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ var evt = {
+ deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0,
+ deltaY: 1.0,
+ lineOrPageDeltaX: 1,
+ lineOrPageDeltaY: 1
+ };
+ // wait scrolled actually.
+ sendWheelAndWait(10, 10, evt, function () {
+ ok(Math.abs(gScrollableElement.scrollLeft - scrolledX) <= 1,
+ "doTestZoomedScroll: zoomed horizontal scroll amount by line wheel event is different from normal, scrollLeft=" +
+ gScrollableElement.scrollLeft + ", scrolledX=" + scrolledX);
+ ok(Math.abs(gScrollableElement.scrollTop - scrolledY) <= 1,
+ "doTestZoomedScroll: zoomed vertical scroll amount by line wheel event is different from normal, scrollTop=" +
+ gScrollableElement.scrollTop + ", scrolledY=" + scrolledY);
+
+ window.removeEventListener("MozMousePixelScroll", handler, true);
+
+ synthesizeKey("0", { accelKey: true });
+ onZoomReset(aCallback);
+ });
+ }, 20);
+ });
+ }, 20);
+ }
+
+ // XXX It's too difficult to test page scroll because the page scroll amount
+ // is computed by complex logic.
+
+ prepareTestZoomedPixelScroll();
+}
+
+function doTestWholeScroll(aCallback)
+{
+ SpecialPowers.pushPrefEnv({"set": [
+ ["mousewheel.default.delta_multiplier_x", 999999],
+ ["mousewheel.default.delta_multiplier_y", 999999]]},
+ function() { doTestWholeScroll2(aCallback); });
+}
+
+function doTestWholeScroll2(aCallback)
+{
+ const kTests = [
+ { description: "try whole-scroll to top (line)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+ expectedScrollTop: 0,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to top when scrollTop is already top-most (line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: -1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+ expectedScrollTop: 0,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to bottom (line)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.0, deltaY: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+ expectedScrollTop: gScrollableElement.scrollTopMax,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to bottom when scrollTop is already bottom-most (line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0, deltaY: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+ expectedScrollTop: gScrollableElement.scrollTopMax,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to left (line)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: 0
+ },
+ { description: "try whole-scroll to left when scrollLeft is already left-most (line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: -1.0, deltaY: 0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: 0
+ },
+ { description: "try whole-scroll to right (line)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: gScrollableElement.scrollLeftMax
+ },
+ { description: "try whole-scroll to right when scrollLeft is already right-most (line)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: gScrollableElement.scrollLeftMax
+ },
+
+
+ { description: "try whole-scroll to top (pixel)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 0,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to top when scrollTop is already top-most (pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: -1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 0,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to bottom (pixel)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0.0, deltaY: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: gScrollableElement.scrollTopMax,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to bottom when scrollTop is already bottom-most (pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 0, deltaY: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: gScrollableElement.scrollTopMax,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to left (pixel)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: 0
+ },
+ { description: "try whole-scroll to left when scrollLeft is already left-most (pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: -1.0, deltaY: 0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: 0
+ },
+ { description: "try whole-scroll to right (pixel)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: gScrollableElement.scrollLeftMax
+ },
+ { description: "try whole-scroll to right when scrollLeft is already right-most (pixel)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PIXEL,
+ deltaX: 1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: gScrollableElement.scrollLeftMax
+ },
+
+
+ { description: "try whole-scroll to top (page)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+ expectedScrollTop: 0,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to top when scrollTop is already top-most (page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: -1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: -1 },
+ expectedScrollTop: 0,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to bottom (page)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0.0, deltaY: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+ expectedScrollTop: gScrollableElement.scrollTopMax,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to bottom when scrollTop is already bottom-most (page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 0, deltaY: 1.0,
+ lineOrPageDeltaX: 0, lineOrPageDeltaY: 1 },
+ expectedScrollTop: gScrollableElement.scrollTopMax,
+ expectedScrollLeft: 1000
+ },
+ { description: "try whole-scroll to left (page)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: 0.0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: 0
+ },
+ { description: "try whole-scroll to left when scrollLeft is already left-most (page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: -1.0, deltaY: 0,
+ lineOrPageDeltaX: -1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: 0
+ },
+ { description: "try whole-scroll to right (page)",
+ prepare () {
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ },
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: gScrollableElement.scrollLeftMax
+ },
+ { description: "try whole-scroll to right when scrollLeft is already right-most (page)",
+ event: { deltaMode: WheelEvent.DOM_DELTA_PAGE,
+ deltaX: 1.0, deltaY: 0.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 0 },
+ expectedScrollTop: 1000,
+ expectedScrollLeft: gScrollableElement.scrollLeftMax
+ },
+ ];
+
+ var index = 0;
+
+ function doIt()
+ {
+ const kTest = kTests[index];
+ if (kTest.prepare) {
+ kTest.prepare();
+ }
+ sendWheelAndWait(10, 10, kTest.event, function () {
+ is(gScrollableElement.scrollTop, kTest.expectedScrollTop,
+ "doTestWholeScroll, " + kTest.description + ": unexpected scrollTop");
+ is(gScrollableElement.scrollLeft, kTest.expectedScrollLeft,
+ "doTestWholeScroll, " + kTest.description + ": unexpected scrollLeft");
+ if (++index == kTests.length) {
+ SimpleTest.executeSoon(aCallback);
+ } else {
+ doIt();
+ }
+ });
+ }
+ doIt();
+}
+
+function doTestActionOverride(aCallback)
+{
+ const kNoScroll = 0x00;
+ const kScrollUp = 0x01;
+ const kScrollDown = 0x02;
+ const kScrollLeft = 0x04;
+ const kScrollRight = 0x08;
+
+ const kTests = [
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNoOverride,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNone,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXScroll,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXNoOverride,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXNone,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXScroll,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNoOverride,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.5,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNone,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.5,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXScroll,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.5,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXNoOverride,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.5,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXNone,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.5,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXScroll,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 1.0, deltaY: 0.5,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNoOverride,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXNone,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionScroll, override_x: kDefaultActionOverrideXScroll,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kScrollDown | kScrollRight
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXNoOverride,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXNone,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ { action: kDefaultActionNone, override_x: kDefaultActionOverrideXScroll,
+ event: { deltaMode: WheelEvent.DOM_DELTA_LINE,
+ deltaX: 0.5, deltaY: 1.0,
+ lineOrPageDeltaX: 1, lineOrPageDeltaY: 1 },
+ expected: kNoScroll
+ },
+ ];
+
+ var index = 0;
+
+ function doIt()
+ {
+ const kTest = kTests[index];
+ SpecialPowers.pushPrefEnv({"set": [
+ ["mousewheel.default.action", kTest.action],
+ ["mousewheel.default.action.override_x", kTest.override_x]]},
+ doIt2
+ );
+ }
+
+ function doIt2()
+ {
+ const kTest = kTests[index];
+ description = "doTestActionOverride(action=" + kTest.action + ", " +
+ "override_x=" + kTest.override_x + ", " +
+ "deltaX=" + kTest.event.deltaX + ", " +
+ "deltaY=" + kTest.event.deltaY + "): ";
+ gScrollableElement.scrollTop = 1000;
+ gScrollableElement.scrollLeft = 1000;
+ sendWheelAndWait(10, 10, kTest.event, function () {
+ if (kTest.expected & kScrollUp) {
+ ok(gScrollableElement.scrollTop < 1000, description + "not scrolled up, got " + gScrollableElement.scrollTop);
+ } else if (kTest.expected & kScrollDown) {
+ ok(gScrollableElement.scrollTop > 1000, description + "not scrolled down, got " + gScrollableElement.scrollTop);
+ } else {
+ is(gScrollableElement.scrollTop, 1000, description + "scrolled vertical");
+ }
+ if (kTest.expected & kScrollLeft) {
+ ok(gScrollableElement.scrollLeft < 1000, description + "not scrolled to left, got " + gScrollableElement.scrollLeft);
+ } else if (kTest.expected & kScrollRight) {
+ ok(gScrollableElement.scrollLeft > 1000, description + "not scrolled to right, got " + gScrollableElement.scrollLeft);
+ } else {
+ is(gScrollableElement.scrollLeft, 1000, description + "scrolled horizontal");
+ }
+ if (++index == kTests.length) {
+ SimpleTest.executeSoon(aCallback);
+ } else {
+ doIt();
+ }
+ });
+ }
+ doIt();
+}
+
+function runTests()
+{
+ SpecialPowers.pushPrefEnv({"set": [
+ ["test.events.async.enabled", true],
+ ["general.smoothScroll", false],
+ ["mousewheel.default.action", kDefaultActionScroll],
+ ["mousewheel.default.action.override_x", kDefaultActionOverrideXNoOverride],
+ ["mousewheel.with_shift.action", kDefaultActionHorizontalizedScroll],
+ ["mousewheel.with_shift.action.override_x", kDefaultActionOverrideXNoOverride],
+ ["mousewheel.with_control.action", kDefaultActionZoom],
+ ["mousewheel.with_control.action.override_x", kDefaultActionOverrideXNoOverride],
+ ["mousewheel.with_alt.action", kDefaultActionHistory],
+ ["mousewheel.with_alt.action.override_x", kDefaultActionOverrideXNoOverride]]},
+ runTests2);
+}
+
+function runTests2()
+{
+ const kSettings = [
+ { description: "all delta values are not customized",
+ deltaMultiplierX: 1.0, deltaMultiplierY: 1.0, deltaMultiplierZ: 1.0 },
+ { description: "deltaX is reverted",
+ deltaMultiplierX: -1.0, deltaMultiplierY: 1.0, deltaMultiplierZ: 1.0 },
+ { description: "deltaY is reverted",
+ deltaMultiplierX: 1.0, deltaMultiplierY: -1.0, deltaMultiplierZ: 1.0 },
+ // Unless you really don't care a snap on time-consuming testing or a strict
+ // criteria is required for testing, it is strongly recommeneded that you
+ // comment the unrealistic case out.
+ //{ description: "deltaZ is reverted",
+ // deltaMultiplierX: 1.0, deltaMultiplierY: 1.0, deltaMultiplierZ: -1.0 },*/
+ { description: "deltaX is 2.0",
+ deltaMultiplierX: 2.0, deltaMultiplierY: 1.0, deltaMultiplierZ: 1.0 },
+ { description: "deltaY is 2.0",
+ deltaMultiplierX: 1.0, deltaMultiplierY: 2.0, deltaMultiplierZ: 1.0 },
+ // Unless you really don't care a snap on time-consuming testing or a strict
+ // criteria is required for testing, it is strongly recommeneded that you
+ // comment the unrealistic case out.
+ //{ description: "deltaZ is 2.0",
+ // deltaMultiplierX: 1.0, deltaMultiplierY: 1.0, deltaMultiplierZ: 2.0 },
+ //{ description: "deltaX is -2.0",
+ // deltaMultiplierX: -2.0, deltaMultiplierY: 1.0, deltaMultiplierZ: 1.0 },
+ //{ description: "deltaY is -2.0",
+ // deltaMultiplierX: 1.0, deltaMultiplierY: -2.0, deltaMultiplierZ: 1.0 },
+ //{ description: "deltaZ is -2.0",
+ // deltaMultiplierX: 1.0, deltaMultiplierY: 1.0, deltaMultiplierZ: -2.0 },
+ ];
+
+ var index = 0;
+
+ function doTest() {
+ setDeltaMultiplierSettings(kSettings[index], function () {
+ doTestScroll(kSettings[index], function () {
+ doTestAutoDirScroll(kSettings[index], {honoursRoot: false}, function () {
+ doTestAutoDirScroll(kSettings[index], {honoursRoot: true}, function () {
+ doTestHorizontalizedScroll(kSettings[index], function() {
+ doTestZoom(kSettings[index], function() {
+ if (++index == kSettings.length) {
+ setDeltaMultiplierSettings(kSettings[0], function() {
+ doTestZoomedScroll(function() {
+ doTestWholeScroll(function() {
+ doTestActionOverride(function() {
+ finishTests();
+ });
+ });
+ });
+ });
+ } else {
+ doTest();
+ }
+ });
+ });
+ });
+ });
+ });
+ });
+ }
+ doTest();
+}
+
+function finishTests()
+{
+ winUtils.restoreNormalRefresh();
+
+ window.opener.finish();
+}
+
+</script>
+</pre>
+</body>
+</html>